Realm for Swift

Realm Studio

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

下载连接git

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 文件来编写工程。

动态库

下载地址github

前往 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,所以这一步在打包通用二进制文件时是必须的。swift

使用RealmSwift

数据模型

1.建立数据模型

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

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、Int八、Int1六、Int3二、Int6四、Double、Float、String、Date 以及 Data。 其中String、Date 以及 Data 属性都是可空的,Object 属性必须可空。app

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

// 可选 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 版本(包括 Int八、Int1六、Int3二、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()
Int @objc dynamic var value = 0 let value = RealmOptional()
Float @objc dynamic var value: Float = 0.0 let value = RealmOptional()
Double @objc dynamic var value: Double = 0. let value = RealmOptional()
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() 不存在:必须是非可空值
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. 中文文档

谢谢

相关文章
相关标签/搜索