Realm Studio
使用 Realm Studio,讓開發更有效率。
您可以輕鬆地開啟任何 Realm 資料庫檔案或者 Realm 物件伺服器部署物件,並對其進行分析。Realm Studio 允許您輕鬆地開啟並編輯本地 Realm 資料庫和可同步 Realm 資料庫,此外還可以管理 Realm 平臺。可執行在 Mac、Windows 以及 Linux 平臺。
專案中新增RealmSwift
Carthage
- Cartfile新增
github "realm/realm-cocoa"
; - 終端中cd到工程檔案;
- 執行
carthage update --platform iOS
- 在
Carthage/Build/iOS/
中將Realm.framework
、RealmSwift.framework
新增到Xcode 工程的 “General” 設定選項卡的 “Linked Frameworks and Libraries” 部分內; - 路徑配置如下圖。Carthage傳送門
CocoaPods
- 執行 pod repo update,從而讓 CocoaPods 更新至目前最新可用的 Realm 版本;
- 在您的 Podfile 中,將 use_frameworks! 和 pod `RealmSwift` 新增到主應用目標和測試目標中;
- 在命令列中執行 pod install;
- 使用由 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() |
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("開啟或者建立資料庫失敗:
(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("開啟或者建立資料庫失敗:
(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的資料庫失敗:
(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的資料庫失敗:
(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("新增資料失敗:
(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("新增資料失敗:
(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("新增資料失敗:
(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封裝了一層。
專案地址
參考資料
謝謝