Realm for Swift

作者: 我要在河边钓一整天的鱼 | 来源:发表于2017-12-29 12:18 被阅读76次

    Realm Studio

    使用 Realm Studio,让开发更有效率。
    您可以轻松地打开任何 Realm 数据库文件或者 Realm 对象服务器部署对象,并对其进行分析。Realm Studio 允许您轻松地打开并编辑本地 Realm 数据库和可同步 Realm 数据库,此外还可以管理 Realm 平台。可运行在 Mac、Windows 以及 Linux 平台。

    下载链接

    Realm 开发工具

    项目中添加RealmSwift

    Carthage

    1. Cartfile添加github "realm/realm-cocoa"
    2. 终端中cd到工程文件;
    3. 运行 carthage update --platform iOS
    4. Carthage/Build/iOS/中将Realm.frameworkRealmSwift.framework添加到Xcode 工程的 “General” 设置选项卡的 “Linked Frameworks and Libraries” 部分内;
    5. 路径配置如下图。Carthage传送门
    配置Carthage路径

    CocoaPods

    1. 执行 pod repo update,从而让 CocoaPods 更新至目前最新可用的 Realm 版本;
    2. 在您的 Podfile 中,将 use_frameworks! 和 pod 'RealmSwift' 添加到主应用目标和测试目标中;
    3. 在命令行中执行 pod install;
    4. 使用由 CocoaPods 生成的 .xcworkspace 文件来编写工程。

    动态库

    下载地址

    前往 Xcode 工程的 “General” 设置选项卡中,在 ios/、osx/、tvos/ 或者 watchos/ 目录中选择适合您项目的 Swift 版本目录,将 Realm.framework 和 RealmSwift.framework 拖曳到 “Embedded Binaries” 部分内。请确保勾选了 Copy items if needed(除非项目中有多个平台都需要使用 Realm ),然后单击 Finish 按钮;
    在单元测试目标的 “Build Settings” 中,将 RealmSwift.framework 的父目录添加到 “Framework Search Paths” 部分中;
    如果在 iOS、watchOS 或者 tvOS 工程中使用 Realm,请在应用目标的 “Build Phases” 中创建一条新的 “Run Script Phase”,然后将下面这段代码粘贴到脚本文本框内:

    Copy to clipboardbash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"
    因为要绕过App Store 出现的提交 bug,因此这一步在打包通用二进制文件时是必须的。

    使用RealmSwift

    数据模型

    1.创建数据模型

    在使用Realm中存储的数据模型都要是Object类的子类。

    import Foundation
    import RealmSwift
    
    class Dog: Object {
       @objc dynamic var id: Int = 0
       @objc dynamic var name: String?
        
    }
    
    class Cat: Object {
        @objc dynamic var name: String?
    }
    
    

    @objc是为了兼容Swift4

    2.支持的属性类型

    Realm支持的类型:Bool、Int、Int8、Int16、Int32、Int64、Double、Float、String、Date 以及 Data。
    其中String、Date 以及 Data 属性都是可空的,Object 属性必须可空。

    在上面Dog中你会发现数值类型是不能写成Int?类型的,在Realm中可选数值类型要如下设置:

      // 可选 int 属性,默认为 nil
      // RealmOption 属性应该始终用 `let` 进行声明,
      // 因为直接对其进行赋值并不会起任何作用
      let age = RealmOptional<Int>()
        
    
    let realm = try! Realm()
    try! realm.write() {
        var person = realm.create(Person.self, value: ["Jane", 27])
        // // 读取或者修改 `RealmOptional` 可以通过 `value` 属性实现
        person.age.value = 28
    }
    
    

    RealmOptional 支持 Int、Float、Double、Bool,以及所有大小的 Int 版本(包括 Int8、Int16、Int32、Int64)。

    3.设置主键

    override static func primaryKey() -> String? {
        return "id"
    }
    

    实现这个方法就能直接设置主键

    4.索引属性

    override static func indexedProperties() -> [String] {
       return ["title"]
    }    
    

    实现indexedProperties进行设置索引属性。

    Realm 支持为字符串、整型、布尔值以及 Date 属性建立索引。

    5.属性备忘单

    类型 非可空值形式 可空值形式
    Bool @objc dynamic var value = false let value = RealmOptional<Bool>()
    Int @objc dynamic var value = 0 let value = RealmOptional<Int>()
    Float @objc dynamic var value: Float = 0.0 let value = RealmOptional<Float>()
    Double @objc dynamic var value: Double = 0. let value = RealmOptional<Double>()
    String @objc dynamic var value = "" @objc dynamic var value: String? = nil
    Data @objc dynamic var value = Data() @objc dynamic var value: Data? = nil
    Date @objc dynamic var value = Date() @objc dynamic var value: Date? = ni
    Object 不存在:必须是可空值 @objc dynamic var value: Class?
    List let value = List<Type>() 不存在:必须是非可空值
    LinkingObjects let value = LinkingObjects(fromType: Class.self, property: "property") 不存在:必须是非可空值

    创建Realm

    //MARK: - 创建数据库
        /// 创建数据库
        ///
        /// - Parameters:
        ///   - dataBaseName: 数据库名字
        ///   - isReadOnly: 是否是只读
        /// - Returns: Realm实例
        @discardableResult
        public func creatDB(_ dataBaseName: String, isReadOnly: Bool = false) -> Realm? {
            let realm = openDB(dataBaseName, isReadOnly: isReadOnly)
            return realm
        }
        
        /// 打开数据库
        ///
        /// - Parameter name: 数据库名字
        /// - isReadOnly: 是否是只读
        /// - Returns: Realm实例
        @discardableResult
        private func openDB(_ dataBaseName: String, isReadOnly: Bool = false) -> Realm? {
            guard let dbPath = getCreatDatabasePath(dataBaseName) else {
                return nil
            }
            var config = Realm.Configuration()
            config.fileURL = dbPath
            config.readOnly = isReadOnly
            Realm.Configuration.defaultConfiguration = config
            do {
                let realm = try Realm.init(configuration: config)
                return realm
            }catch let error {
                mPrint("打开或者创建数据库失败:\n\(error.localizedDescription)")
                return nil
            }
        }
    

    在本地生成realm.realm文件时还会有:

    • realm.lock - 资源锁定文件;
    • realm.management - 存放进程锁文件的目录;
    • realm.note - 用于通知的命名管道。
      这些文件只是Realm维护的文件删除或者怎么着都不会出现什么问题。

    在报告 Realm 问题的时候,请将这些辅助文件 (auxiliary Realm) 连同主要的 .realm 文件一同提交,因为它们很可能会包含某些对调试问题有用的信息。

    打开创建的文件利用RealmStudioda打开会发现,在工程中所有继承Object的类直接在Realm中创建了表(暂时未找到具体的原因)。

    打开工程中的Realm文件

        /// 打开预植的数据库
        ///
        /// - Parameters:
        ///   - dataBaseName: 数据库名字
        ///   - isReadOnly: 是否是只读
        /// - Returns: Realm实例
        @discardableResult
        public func openReferenceDB(_ dataBaseName: String, isReadOnly: Bool = true) -> Realm? {
            guard let dbPath = getReferenceDatabasePaeh(dataBaseName) else {
                return nil
            }
            var config = Realm.Configuration()
            config.fileURL = dbPath
            config.readOnly = isReadOnly
            Realm.Configuration.defaultConfiguration = config
            do {
                let realm = try Realm.init(configuration: config)
                return realm
            }catch let error {
                mPrint("打开或者创建数据库失败:\n\(error.localizedDescription)")
                return nil
            }
        }
    

    设置Realm的defaultConfiguration

        /// 设置通过Realm()获取数据库的配置
        ///
        /// - Parameters:
        ///   - realmName: 数据库的名字
        ///   - isReadOnly: 是否是这是只读
        public func setDefaltRealmConfiguration(_ realmName: String,isReference: Bool = false, isReadOnly: Bool = false) -> Bool{
            var realmPath: URL?
            if isReference {
                realmPath = getReferenceDatabasePaeh(realmName)
            }else {
                realmPath = getCreatDatabasePath(realmName)
            }
            if realmPath == nil {
                return false
            }
            var config = Realm.Configuration()
            config.fileURL = realmPath
            config.readOnly = isReadOnly
            Realm.Configuration.defaultConfiguration = config
            return true
        }
    

    获取当前默认的数据库

        /// 获取当前默认的数据
        ///
        /// - Returns: 返回默认的Realm的数据库实例
        @discardableResult
        public func getDefaultRealm() -> Realm? {
            do {
                return try Realm()
            }catch let error {
                mPrint("获取默认的Realm的数据库失败:\n\(error.localizedDescription)")
                return nil
            }
        }
    

    如果没有配置defaultConfiguration则会获取默认的数据库。

    /Library/Developer/CoreSimulator/Devices
    /26B4D5CC-1EF4-4897-8F02-BCFBE06F7C40/data
    /Containers/Data/Application/7CDCBAF4-A7A2-45E4-9B8A-725E873975AD/Documents/default.realm
    

    配置后会获取到设置路径的数据库。

    /Library/Developer/CoreSimulator/Devices
    /26B4D5CC-1EF4-4897-8F02-BCFBE06F7C40/data
    /Containers/Data/Application/E050DEE4-71FB-4866-A10C-CBADA288D35C/Library/Caches/DB/2237DB/2237DB.realm
    

    Realm的实例不用全局数据共享,在配置默认数据库后你无论在什么地方获取的Realm()都是同一个数据库。

        //MARK: - 增
        /// 创建表 || 更新表
        ///
        /// - Parameters:
        ///   - type: 表向对应的对象
        ///   - value: 值
        ///   - update: 是否是更新, 如果是"true", Realm会查找对象并更新它, 否则添加对象
        ///   - result: 最后添加对象是成功, 如果成功将对象返回
        public func creatObject(_ type: RealmSwift.Object.Type, value: Any? = nil, update: Bool = false, result: ((RealmSwift.Object?, Error?) -> Void)? = nil){
            let realm = getDefaultRealm()
            do {
                try realm?.write {
                    let object = (value == nil) ? realm?.create(type) : realm?.create(type, value: value!, update: update)
                    result?(object, nil)
                }
            } catch let error {
                mPrint("获取默认的Realm的数据库失败:\n\(error.localizedDescription)")
                result?(nil, error)
            }
        }
        
    
        /// 添加数据 || 根据主键更新数据
        ///
        /// - Parameters:
        ///   - object: 要添加的数据
        ///   - update: 是否更新, 如果是true
        ///   - result: 添加数据的状态
        public func addObject(_ object: RealmSwift.Object, update: Bool = false, result: ((Error?) -> Void)? = nil) {
            let realm = getDefaultRealm()
            do {
                try realm?.write {
                    realm?.add(object, update: update)
                    result?(nil)
                }
            } catch let error {
                mPrint("添加数据失败:\n \(error.localizedDescription)")
                result?(error)
            }
        }
    

    //MARK: - 删
        /// 删除数据
        ///
        /// - Parameters:
        ///   - object: 要删除的对象
        ///   - result: 删除的状态
        public func deleteObject(_ object: RealmSwift.Object, result: ((Error?) -> Void)? = nil) {
            let realm = getDefaultRealm()
            do {
                try realm?.write {
                    realm?.delete(object)
                    result?(nil)
                }
            } catch let error {
                mPrint("添加数据失败:\n \(error.localizedDescription)")
                result?(error)
            }
        }
    
        
        /// 删除当前数据库中所有的数据
        ///
        /// - Parameter result: 删除的状态
        public func deleteAllObject(result: ((Error?) -> Void)? = nil) {
            let realm = getDefaultRealm()
            do {
                try realm?.write {
                    realm?.deleteAll()
                    result?(nil)
                }
            } catch let error {
                mPrint("添加数据失败:\n \(error.localizedDescription)")
                result?(error)
            }
        }
    
        /// 删除当前打开的数据库
        ///
        /// - Parameter dataBaseName: 数据库的名字
        /// - Returns: 删除的状态
        @discardableResult
        public func deleteCreatDBFile() -> Bool {
           return  autoreleasepool { () -> Bool in
                let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
                let realmURLs = [
                    realmURL,
                    realmURL.appendingPathExtension("lock"),
                    realmURL.appendingPathExtension("note"),
                    realmURL.appendingPathExtension("management")
                ]
                for URL in realmURLs {
                    do {
                        try FileManager.default.removeItem(at: URL)
                        return true
                    } catch {
                        // 错误处理
                        return false
                    }
                }
                return false
            }
        }
    

    //MARK: - 改
        /// 根据主键进行更新
        ///
        /// - Parameters:
        ///   - object: 要更新的对象
        ///   - update: 是否根据主键更新, 如果是"false"则是添加数据
        ///   - result: 更新数据的结果
        public func updateObject(_ object: RealmSwift.Object, update: Bool = true, result: ((Error?) -> Void)? = nil) {
            addObject(object, update: update, result: result)
        }
    
        
        /// 根据主键进行更新
        ///
        /// - Parameters:
        ///   - type: 要更新的对象类型
        ///   - value: 要更新的值, 例如: ["id": 1, "price": 9000.0]
        ///   - update: 是否根据主键进行更新, 如果为"false"则为创建表
        ///   - result: 更新的结果
        public func updateObject(_ type: RealmSwift.Object.Type, value: Any? = nil, update: Bool = true, result: ((RealmSwift.Object?, Error?) -> Void)? = nil) {
            creatObject(type, value: value, update: update, result: result)
        }
    
        
        /// 直接更新对象
        ///
        /// - Parameters:
        ///   - property: 要更改的属性
        ///   - value: 更改的值
        /// - Returns: 更改的结果
        @discardableResult
        public func updateObject( property: inout Any, value: Any) -> Bool {
            let realm = getDefaultRealm()
            do {
                try realm?.write {
                     property = value
                }
                return true
            } catch let error {
                mPrint("直接更新对象属性错误: \(error.localizedDescription)")
                return false
            }
        }
    
        
        /// 更改表中所有的字段的值
        ///
        /// - Parameters:
        ///   - type: 表的对象类型
        ///   - key: 要更改的字段名
        ///   - value: 更改的值
        /// - Returns: 返回更改结果
        public func updateObjects(type: RealmSwift.Object.Type, key: String, value: Any) -> Bool {
            let objects = getObjects(type: type)
            do {
                try getDefaultRealm()?.write {
                    objects?.setValue(value, forKeyPath: key)
                }
                return true
            } catch let error {
                mPrint("更改一个表中的所有数据错误: \(error.localizedDescription)")
                return false
            }
        }
    
        
        /// 根据主键进行对某个对象中的数据进行更新
        ///
        /// - Parameters:
        ///   - type: 表类型
        ///   - primaryKey: 主键
        ///   - key: 要更改属性
        ///   - value: 更改的值
        /// - Returns: 更改的状态
        public func updateObject(type: RealmSwift.Object.Type, primaryKey: Any, key: String, value: Any) -> Bool {
            let object = getObjectWithPrimaryKey(type: type, primaryKey: primaryKey)
            do {
                try getDefaultRealm()?.write {
                    object?.setValue(value, forKeyPath: key)
                }
                return true
            } catch let error {
                mPrint("更新数据出错: \(error.localizedDescription)")
                return false
            }
        }
    

    //MARK: - 查
        /// 查找一个表中的所有的数据
        ///
        /// - Parameter type: 对象类型
        /// - Returns: 查到的数据
        public func getObjects(type: RealmSwift.Object.Type) -> RealmSwift.Results<RealmSwift.Object>?{
             return getDefaultRealm()?.objects(type)
        }
    
        /// 根据主键查找某个对象
        ///
        /// - Parameters:
        ///   - type: 对象类型
        ///   - primaryKey: 主键
        /// - Returns: 查到的数据
        public func getObjectWithPrimaryKey(type: RealmSwift.Object.Type, primaryKey: Any) -> RealmSwift.Object? {
            return getDefaultRealm()?.object(ofType: type, forPrimaryKey: primaryKey)
        }
    
        
        /// 使用断言字符串查询
        ///
        /// - Parameters:
        ///   - type: 对象类型
        ///   - filter: 过滤条件
        /// - Returns: 查询到的数据
        /// - example:
        ///   - var tanDogs = realm.objects(Dog.self).filter("color = 'tan' AND name BEGINSWITH 'B'")
        public func getObject(type: RealmSwift.Object.Type, filter: String) -> RealmSwift.Results<RealmSwift.Object>? {
            return getObjects(type: type)?.filter(filter)
        }
    
        
        /// 使用谓词进行查询
        ///
        /// - Parameters:
        ///   - type: 对象类型
        ///   - predicate: 谓词对象
        /// - Returns: 查询到的数据
        /// - example:
        ///   - let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
        ///   - tanDogs = realm.objects(Dog.self).filter(predicate)
        public func getObject(type: RealmSwift.Object.Type, predicate: NSPredicate) -> RealmSwift.Results<RealmSwift.Object>? {
            return getObjects(type: type)?.filter(predicate)
        }
    
    
        /// 对查询的数据进行排序,请注意, 不支持 将多个属性用作排序基准,此外也无法链式排序(只有最后一个 sorted 调用会被使用)。 如果要对多个属性进行排序,请使用 sorted(by:) 方法,然后向其中输入多个 SortDescriptor 对象。
        ///
        /// - Parameters:
        ///   - type: 对象类型
        ///   - filter: 过滤条件
        ///   - sortedKey: 需要排序的字段
        /// - Returns: 最后的结果
        public func getObject(type: RealmSwift.Object.Type, filter: String, sortedKey: String) -> RealmSwift.Results<RealmSwift.Object>? {
            return getObject(type: type, filter: filter)?.sorted(byKeyPath: sortedKey)
        }
    
        
        /// 对查询的数据进行排序, 请注意, 不支持 将多个属性用作排序基准,此外也无法链式排序(只有最后一个 sorted 调用会被使用)。 如果要对多个属性进行排序,请使用 sorted(by:) 方法,然后向其中输入多个 SortDescriptor 对象。
        ///
        /// - Parameters:
        ///   - type: 队形类型
        ///   - predicate: 谓词对象
        ///   - sortedKey: 排序的字段
        /// - Returns: 排序后的数据
        public func getObject(type: RealmSwift.Object.Type, predicate: NSPredicate, sortedKey: String) -> RealmSwift.Results<RealmSwift.Object>? {
            return getObject(type: type, predicate: predicate)?.sorted(byKeyPath: sortedKey)
        }
    

    集合

    Realm 拥有许多能够表示一组对象的类型,称之为 “Realm 集合”:

    • Results 类,表示queries所返回的对象集合。
    • List 类,表示模型之间的对多关系。
    • LinkingObjects 类,表示模型之间的双向关系。
    • RealmCollection 协议,定义了所有 Realm 集合的常用接口。
    • AnyRealmCollection 类,这是一个无类型的类,可以将调用转发给具体的 Realm 集合,例如 Results、List 或者 LinkingObjects。

    Realm 集合类型均实现了 RealmCollection 协议,这确保 它们的行为均保持一致。这个协议继承自 CollectionType,因此它的使用方式 与标准库内的集合相同。这个协议也同样声明了其他常用的 Realm 集合 API, 比如说检索、排序、聚合操作等等。List 还存在一些额外的修改操作, 这些操作没有在协议接口中定义,比如说添加或者删除对象。

    使用 RealmCollection 协议, 您可以编写能够对任意 Realm 集合进行操作的泛型代码:

    Copy to clipboardfunc operateOn<C: RealmCollection>(collection: C) {
        // collection 既可以是 RLMResults,也可以是 RLMArray
        print("operating on collection containing \(collection.count) objects")
    }
    

    由于 Swift 类型系统的限制,必须使用诸如 AnyRealmCollection 之类的无类型封装器,才能将这个集合存储在属性或者变量中:

    Copy to clipboardclass ViewController {
    //    let collection: RealmCollection
    //                    ^
    //                    error: protocol 'RealmCollection' can only be used
    //                    as a generic constraint because it has Self or
    //                    associated type requirements
    //
    //    init<C: RealmCollection>(collection: C) where C.ElementType == MyModel {
    //        self.collection = collection
    //    }
    
        let collection: AnyRealmCollection<MyModel>
    
        init<C: RealmCollection>(collection: C) where C.ElementType == MyModel {
            self.collection = AnyRealmCollection(collection)
        }
    }
    

    Realm的基础使用先写到这里,更详细的可以直接看文档(真的很详细)。

    在使用RealmSwift增删改查又用RxSwift封装了一层。
    项目地址

    参考资料

    1. 英文文档
    2. 中文文档

    谢谢

    相关文章

      网友评论

        本文标题:Realm for Swift

        本文链接:https://www.haomeiwen.com/subject/mnsigxtx.html