RealmTools.swift 19.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
//
//  RealmTools.swift
//  Twear
//
//  Created by yangbin on 2021/11/22.
//
import UIKit
import RealmSwift

/**
 Realm 致力于平衡数据库读取的灵活性和性能为了实现这个目标 Realm 中所存储的信息的各个方面都有基本的限制例如
 1类名称的长度最大只能存储 57  UTF8 字符
 2属性名称的长度最大只能支持 63  UTF8 字符
 3NSData 以及 String 属性不能保存超过 16 MB 大小的数据如果要存储大量的数据可通过将其分解为16MB 大小的块或者直接存储在文件系统中然后将文件路径存储在 Realm 如果您的应用试图存储一个大于 16MB 的单一属性系统将在运行时抛出异常
 4对字符串进行排序以及不区分大小写查询只支持基础拉丁字符集”、“拉丁字符补充集”、“拉丁文扩展字符集 A 以及拉丁文扩展字符集 B“(UTF-8 的范围在 0~591 之间)。
 */

// 事件闭包(做完Realm操作后的事件)
public typealias RealmDoneTask = () -> Void
class RealmTools: NSObject {
    /// 单粒
    static let sharedInstance = RealmTools()
    private var config: Realm.Configuration?
    /// 当前的 Realm
    fileprivate var currentRealm: Realm? {
        return try? Realm(configuration: config!)
    }
    /// 当前realm存储的路径
    static var fileURL: URL? {
        return sharedInstance.currentRealm?.configuration.fileURL
    }
    /// 当前的版本号
    fileprivate var currentSchemaVersion: UInt64 = 0
    /// 当前的加密字符串
    fileprivate var currentKeyWord: String? = ""
    
//    private let dbQueue = DispatchQueue(label: "com.codepgq.github", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
//       //DispatchQueue(label:"com.appcoda.queue2", qos:DispatchQoS.userInitiated)
//       func execQueueSQL(action : @escaping (_ manager : RealmTools) ->()){
//           //开一个子线程
//           DispatchQueue.global().async {
//               action(self)
//           }
//       }
//
}

// MARK:- Realm数据库配置和版本差异化配置
/**
 通过调用 Realm() 来初始化以及访问我们的 realm 变量其指向的是应用的 Documents 文件夹下的一个名为default.realm的文件
 通过对默认配置进行更改我们可以使用不同的数据库比如给每个用户帐号创建一个特有的 Realm 文件通过切换配置就可以直接使用默认的 Realm 数据库来直接访问各自数据库
 */
// 在(application:didFinishLaunchingWithOptions:)中进行配置
extension RealmTools {
    
//    public class func configRealm() {
//           /// 这个方法主要用于数据模型属性增加或删除时的数据迁移,每次模型属性变化时,将 dbVersion 加 1 即可,Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构,移除属性的数据将会被删除。
//           let dbVersion : UInt64 = 1
//           let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
//           let dbPath = docPath.appending("/defaultDB.realm")
//           let config = Realm.Configuration(fileURL: URL.init(string: dbPath), inMemoryIdentifier: nil, syncConfiguration: nil, encryptionKey: nil, readOnly: false, schemaVersion: dbVersion, migrationBlock: { (migration, oldSchemaVersion) in
//
//           }, deleteRealmIfMigrationNeeded: false, shouldCompactOnLaunch: nil, objectTypes: nil)
//           Realm.Configuration.defaultConfiguration = config
////        Realm.asyncOpen(configuration: config) { (realm, error) in
////            if let _ = realm {
////                print("Realm 服务器配置成功!")
////            }else if let error = error {
////                print("Realm 数据库配置失败:\(error.localizedDescription)")
////            }
////        }
//
//
//       }
    
    // MARK: 配置数据库,为用户提供个性化的 Realm 配置(加密暂时没有使用)
    /// 配置数据库,为用户提供个性化的 Realm 配置
    /// - Parameters:
    ///   - userID: 用户的ID
    ///   - keyWord: 加密字符串
    ///   - schemaVersion: 设置新的架构版本(如果要存储的数据模型属性发生变化),这个版本号必须高于之前所用的版本号,如果您之前从未设置过架构版本,那么这个版本号设置为 0)
    ///   
    static func configRealm(migrationBlock: MigrationBlock? = nil) {
        let currentVersion: UInt64 = 1
        let config = Realm.Configuration(fileURL: URL.init(string: getRealmPath()), inMemoryIdentifier: nil, syncConfiguration: nil, encryptionKey: nil, readOnly: false, schemaVersion: currentVersion, migrationBlock: { (migration, oldSchemaVersion) in
                    
                }, deleteRealmIfMigrationNeeded: false, shouldCompactOnLaunch: nil, objectTypes: nil)
                Realm.Configuration.defaultConfiguration = config

                Realm.asyncOpen { (result) in
                    switch result {
                       case .success(_):
                        print("Realm 服务器配置成功!")
                       case .failure(let error):
                        print("Realm 数据库配置失败:\(error.localizedDescription)")

                          
                       }
                }
    
//        let config = Realm.Configuration(fileURL: URL(string: getRealmPath()), schemaVersion: currentVersion, migrationBlock: { (migration, oldSchemaVersion) in
//            // 目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
//            if oldSchemaVersion < 1 {
//                // 什么都不要做!Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
//            }
//            // 低版本的数据库迁移......
//            if migrationBlock != nil {
//                migrationBlock!(migration, oldSchemaVersion)
//            }
//        })
//        // 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
//        Realm.Configuration.defaultConfiguration = config
//        guard let realm = try? Realm(configuration: config) else {
//            return
//        }
        sharedInstance.currentSchemaVersion = currentVersion
        sharedInstance.config = config
//        sharedInstance.currentRealm = realm
//        sharedInstance.currentKeyWord = keyWord
    }
    
    
    
    // MARK: 删除当前的realm库
    /// 删除当前的realm库
    @discardableResult
    static func deleteRealmFiles() -> Bool {
        let realmURL = sharedInstance.currentRealm?.configuration.fileURL ?? Realm.Configuration.defaultConfiguration.fileURL!
        let realmURLs = [
            realmURL,
            realmURL.appendingPathExtension("lock"),
            realmURL.appendingPathExtension("management")
        ]
        for URL in realmURLs {
            do {
                try FileManager.default.removeItem(at: URL)
                self.configRealm()
            } catch {
                // handle error
                return false
            }
        }
        return true
    }
    
    private class func getRealmPath() -> String{
        
        
//        let filePathDaildata = "\(Bundle.main.resourcePath!)/wyp.realm"
////        NSString *dialDirectoryPath = [NSString stringWithFormat:@"%@/%@/%@/%d",filePathDaildata,@"dial",@"DialDataRtk",2];
////        dialDirectoryPath = [NSString stringWithFormat:@"%@/%@",dialDirectoryPath,@"dial.bin"];
//        return "/Users/yangbin/Desktop/Twear.realm"
        
         let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
 
         let name : String = "Twear"
         let dbPath = docPath.appending("/\(name).realm")
 //        print("realm地址为: \(dbPath)")
         return dbPath
     }
}

// MARK:- 增
extension RealmTools {
    
    // MARK: 添加单个对象
    /// 添加单个对象
    /// - Parameters:
    ///   - object: 对象
    ///   - update: 是否更新
    ///   - task: 添加后操作
    static func add(_ object: Object, update: Realm.UpdatePolicy = .error) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.add(object, update: update)
        }
    }

    
    // MARK: 添加多个对象
    /// 添加多个对象
    /// - Parameters:
    ///   - objects: 对象组
    ///   - update: 是否更新
    ///   - task: 添加后操作
    static func addList(_ objects: Array<Object>, update: Realm.UpdatePolicy = .error) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.add(objects, update: update)
        }
    }
}

// MARK:- 删
extension RealmTools {
    
    // MARK: 在事务中删除一个对象
    /// 在事务中删除一个对象
    /// - Parameters:
    ///   - object: 单个被删除的对象
    ///   - task: 删除后操作
    static func delete(_ object: Object) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.delete(object)
        }
    }
    
    // MARK: 在事务中删除多个对象
    /// 在事务中删除多个对象
    /// - Parameters:
    ///   - objects: 多个要被删除的对象
    ///   - task: 删除后操作
    static func deleteList(_ objects: Array<Object>) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.delete(objects)
        }
    }
    
    // MARK: 删除所有数据(不要轻易调用)
    /// 从 Realm 中删除所有数据
    /// - Parameter task: 删除后操作
    static func deleteAll(task: @escaping RealmDoneTask) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.deleteAll()
            task()
        }
    }
    
    // MARK: 根据条件删除对象
    /// 根据条件删除对象
    /// - Parameters:
    ///   - object: 对象类型
    ///   - predicate: 条件
    static func deleteByPredicate(object: Object.Type, filter: String) {
        guard let results: Array<Object> = queryObjects(object, filter: filter) else {
            return
        }
        deleteList(results)
    }
}

// MARK:- 改
extension RealmTools {
    
    // MARK: 更改某个对象(根据主键存在来更新,元素必须有主键)
    /// 更改某个对象(根据主键存在来更新)
    /// - Parameters:
    ///   - object: 某个对象
    ///   - update: 是否更新
    static func update(object: Object, update: Realm.UpdatePolicy = .modified) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.add(object, update: update)
        }
    }

    // MARK: 更改多个对象(根据主键存在来更新,元素必须有主键)
    /// 更改多个对象(根据主键存在来更新)
    /// - Parameters:
    ///   - objects: 多个对象
    ///   - update: 是否更新
    static func updateList(objects: Array<Object>, update: Realm.UpdatePolicy = .modified) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            weakCurrentRealm.add(objects, update: .modified)
        }
    }
    
    // MARK: 更新操作,对于realm搜索结果集当中的元素,在action当中直接赋值即可修改(比如查询到的某些属性可直接修改)
    /// 更新操作,对于realm搜索结果集当中的元素,在action当中直接赋值即可修改
    /// - Parameter action: 操作
    static func updateWithTranstion(action: (Bool) -> Void) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        try? weakCurrentRealm.write {
            action(true)
        }
    }
    
    // MARK: 更新一个一个对象的多个属性值(根据主键存在来更新,元素必须有主键)
    /// 更新一个一个对象的多个属性值
    /// - Parameters:
    ///   - object: 对象类型
    ///   - value: 数组数组
    ///   - update: 更新类型
    static func updateObjectAttribute(object: Object.Type, value: Any = [:], update: Realm.UpdatePolicy = .modified) {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return
        }
        do {
            try weakCurrentRealm.write {
                weakCurrentRealm.create(object, value: value, update: update)
            }
        } catch _ {
            
        }
    }
}

// MARK:- 查
extension RealmTools {
    // MARK: 查询某个对象数据
    /// 查询某个对象数据
    /// - Parameter type: 对象类型
    /// - Returns: 返回查询的结果
    /// _ predicateFormat: String, _ args: Any...
    static func queryObjects(_ object: Object.Type, filter: String? = nil, _ argumentArray: [Any]? = nil) -> Array<Object>? {
        var results : Results<Object>?
        if filter != nil {
            results =  queryWith(object: object, filter: filter!, argumentArray)
        }
        else {
            results = queryWithType(object: object)
        }
        if results == nil {
            return nil
        } else {
            return resultsToObjectList(results: results!)
        }
    }
        
    
    // MARK: 查询某个对象数据(根据条件)
    /// 查询某个对象数据(根据条件)
    /// - Parameters:
    ///   - object: 对象类型
    ///   - predicate: 查询条件
    /// - Returns: 返回查询结果
    static func queryObjectsWithPredicate(object: Object.Type, filter: String) -> Array<Object>? {
        guard let results = queryWith(object: object, filter: filter) else {
            return nil
        }
        return resultsToObjectList(results: results)
    }
    
    // MARK: 带排序条件查询
    ///  带排序条件查询
    /// - Parameters:
    ///   - object: 对象类型
    ///   - predicate:  查询条件
    ///   - sortedKey: 排序的键
    ///   - isAssending: 升序还是降序,默认升序
    /// - Returns: 返回查询对象数组
    static func objectsWithPredicateAndSorted(object: Object.Type,
                                           predicate: NSPredicate? = nil,
                                           sortedKey: String,
                                         isAssending: Bool = true) -> Array<Object>? {
        guard let results = queryWithSorted(object: object, predicate: predicate, sortedKey: sortedKey, isAssending: isAssending) else {
            return nil
        }
        return resultsToObjectList(results: results)
    }
    
    
    // MARK: 带分页的查询
    /// 带分页的查询
    /// - Parameters:
    ///   - object: 对象类型
    ///   - predicate: 查询条件
    ///   - sortedKey: 排序的键
    ///   - isAssending: 升序还是降序,默认升序
    ///   - fromIndex: 起始页
    ///   - pageSize: 一页的数量
    /// - Returns: 返回查询对象数组
    static func objectsWithPredicateAndSortedForPages(object: Object.Type,
                                                      predicate: NSPredicate? = nil,
                                                      sortedKey: String,
                                                      isAssending: Bool = true,
                                                      fromIndex: Int = 1,
                                                      pageSize: Int) -> Array<Object>? {
        guard let results = queryWithSorted(object: object,
                                         predicate: predicate,
                                         sortedKey: sortedKey,
                                     isAssending: isAssending) else {
            return nil
        }
        var resultsArray = Array<Object>()
        if results.count <= pageSize * (fromIndex - 1) || fromIndex <= 0 {
            return resultsArray
        }
        if results.count > 0 {
            for i in pageSize * (fromIndex - 1)...(min(fromIndex * pageSize, results.count) - 1) {
                resultsArray.append(results[i])
            }
        }
        return resultsArray
    }
}

//MARK:- 私有(查询)
extension RealmTools {
    
    /// 查询某个对象数据
    /// - Parameter object: 对象类型
    /// - Returns: 返回查询对象数组
    private static func queryWithType(object: Object.Type) -> Results<Object>? {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return nil
        }
        return weakCurrentRealm.objects(object)
    }
    
    // MARK: 根据条件查询数据
    /// 根据条件查询数据
    /// - Parameters:
    ///   - object: 对象类型
    ///   - predicate: 查询条件
    /// - Returns: 返回查询对象数组
    private static func queryWith(object: Object.Type,
                                  filter: String, _ argumentArray: [Any]? = nil) -> Results<Object>? {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return nil
        }
        
        if argumentArray == nil {
            return weakCurrentRealm.objects(object).filter(filter)
        } else {
            return weakCurrentRealm.objects(object).filter(NSPredicate(format: filter, argumentArray: argumentArray))
        }
        
     
        
        
    }
    
    // MARK: 带排序条件查询
    /// 带排序条件查询
    /// - Parameters:
    ///   - object: 对象类型
    ///   - predicate: 查询条件
    ///   - sortedKey: 排序的键
    ///   - isAssending: 升序还是降序,默认升序
    /// - Returns: 返回查询对象数组
    private static func queryWithSorted(object: Object.Type,
                                     predicate: NSPredicate? = nil,
                                     sortedKey: String,
                                   isAssending: Bool = true) -> Results<Object>? {
        guard let weakCurrentRealm = sharedInstance.currentRealm else {
            return nil
        }
        if predicate == nil {
            return weakCurrentRealm.objects(object)
                .sorted(byKeyPath: sortedKey, ascending: isAssending)
        } else {
            return weakCurrentRealm.objects(object).filter(predicate!)
                .sorted(byKeyPath: sortedKey, ascending: isAssending)
        }
       
    }
    
    // MARK: 查询结果转Array<Object>
    /// 查询结果转Array<Object>
    /// - Parameter results: 查询结果
    /// - Returns: 返回Array<Object>
    private static func resultsToObjectList(results: Results<Object>) -> Array<Object> {
        var resultsArray = Array<Object>()
        if results.count > 0 {
            for i in 0...(results.count - 1) {
                resultsArray.append(results[i])
            }
        }
        return resultsArray
    }
}