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. 中文文檔

謝謝

相關文章
相關標籤/搜索