ActiveRealm
ActiveRealm是使用Realm的Objective-C/Swift Active Record库,灵感来源于Ruby on Rails的ActiveRecord。
特性
- Objective-C和Swift制作
- 连接到Realm DB
- 许多读取操作
- 通过一对一或一对多关系级联删除
- ActiveRealm对象可以在多线程中使用
安装
CocoaPods
ActiveRealm可通过CocoaPods获取。要安装它,只需将以下行添加到Podfile中:
pod "ActiveRealm"
手动安装
- 安装Realm
- 下载最新的ActiveRealm
- 将ActiveRealm.framework拖放到您的Xcode项目中
开始使用
-
导入
import ActiveRealm
-
配置 Realm
首先,将您的 Realm 配置为默认的 Realm。详细信息,请参阅这里。
// Configure your Realm configuration as default Realm. let configuration = RLMRealmConfiguration.default() // Something to do RLMRealmConfiguration.setDefault(configuration)
-
实现模型
使用 ARMObject 类实现 Realm 模型。ARMObject 是 RLMObject 的子类。ARMObject 默认拥有以下三个属性:`uid`、`createdAt`、`updatedAt`。`uid` 属性是 ActiveRealm 模型的主键。您必须使用 ARMObject,而不是 RLMObject。
继承 ARMObject 的类名称必须以前缀:`ActiveRealm` 开头。
ARMObject 的子类仅包含保存到数据库中的属性。
class ActiveRealmAuthor: ARMObject { @objc dynamic var name = "" @objc dynamic var age: NSNumber = 0 }
接下来,实现 ARMActiveRealm 子类的模型。继承 ARMActiveRealm 的类名称必须与继承 ARMObject 的类名称去掉 `ActiveRealm` 前缀后的字符串相同。
例如:ActiveRealmAuthor -> Author
class Author: ARMActiveRealm { @objc var name = "" @objc var age: NSNumber = 0 }
[重要]
Now, ActiveRealm does NOT support primitive type properties. e.g.) Int, Float, Double, Bool, etc... Please use NSNumber instead.
关系
您可以通过覆写 `definedRelationships` 方法在两个 ActiveRealm 子类之间建立关系。通过建立关系,可以实现级联删除。使用 ARMRelationship 类和 ARMInverseRelationship 类来建立关系。
一对一
例如,一个 Author 对象有一个 UserSettings 对象。
首先,在 Author 类中,将相关子类设置为 ARMRelationship 对象的 `.hasOne` 类型。
class ActiveRealmAuthor: ARMObject {
@objc dynamic var name = ""
@objc dynamic var age: NSNumber = 0
}
// Parent
class Author: ARMActiveRealm {
@objc var name = ""
@objc var age: NSNumber = 0
override class func definedRelationships() -> [String: ARMRelationship] {
return ["userSettings": ARMRelationship(with: UserSettings.self, type: .hasOne)]
}
}
接下来,在 UserSettings 类中,将相关父类设置为 ARMInverseRelationship 对象的 `.belongsTo` 类型。然后,实现一个 `authorID` 属性作为外键。作为外键的属性名称必须是父类名称,以小写字母开头并在末尾添加 ID。
class ActiveRealmUserSettings: ARMObject {
@objc dynamic var authorID = ""
@objc dynamic var notificationEnabled: NSNumber = false
}
// Child
class UserSettings: ARMActiveRealm {
@objc var authorID = ""
@objc var notificationEnabled: NSNumber = false
override class func definedRelationships() -> [String: ARMRelationship] {
return ["author": ARMInverseRelationship(with: Author.self, type: .belongsTo)]
}
}
一对多
例如,一个 Article 对象有许多 Tag 对象。
首先,在 Article 类中,将相关子类设置为 ARMRelationship 对象的 `.hasMany` 类型。
class ActiveRealmArticle: ARMObject {
@objc dynamic var title = ""
@objc dynamic var text = ""
}
class Article: ARMActiveRealm {
@objc var title = ""
@objc var text = ""
override class func definedRelationships() -> [String: ARMRelationship] {
return ["tags": ARMRelationship(with: Tag.self, type: .hasMany)]
}
}
接下来,您需要在标签类中设置相应的父类以及 .belongsTo
类型到 ARMInverseRelationship 对象。然后,您实现一个 articleID
属性作为外键。外键属性名必须是父类名称,且以小写字母开头,并在末尾添加 ID。
class ActiveRealmTag: ARMObject {
@objc dynamic var articleID = ""
@objc dynamic var name = ""
}
class Tag: ARMActiveRealm {
@objc var articleID = ""
@objc var name = ""
override class func definedRelationships() -> [String: ARMRelationship] {
return ["article": ARMInverseRelationship(with: Article.self, type: .belongsTo)]
}
}
访问相关对象
您可以通过 relations
属性来访问相关对象。这里的 relations
是一个字典,您需要指定与 definedRelationships
方法返回的字典相同的键。
let alice = Author.findOrCreate(["name": "Alice", "age": 28])
UserSettings.findOrCreate(["authorID": alice.uid, "notificationEnabled": true])
// One-to-One
// Use relations[key].object
if let settings = alice.relations["userSettings"]?.object as? UserSettings {
// Something to do.
}
let article = Article.findOrCreate(["title": "ActiveRealm User Guide",
"text": "ActiveRealm is a library for iOS."])
Tag.findOrCreate(["articleID": article.uid, "name": "Programming"])
Tag.findOrCreate(["articleID": article.uid, "name": "iOS"])
// One-to-Many
// Use relations[key].objects
if let tags = article.relations["tags"]?.objects as? [Tag] {
// Something to do.
}
// Inverse relationship
let tag = Tag.find(["articleID": article.uid])
if let article = tag.relations["article"]?.object as? Article {
// Something to do.
}
建议您实现关系属性的别名。
class Author: ARMActiveRealm {
...
// The relation property. This property is just alias.
var userSettings: UserSettings? {
guard let userSettings = relations["userSettings"]?.object as? UserSettings else { return nil }
return userSettings
}
override class func definedRelationships() -> [String: ARMRelationship] {
return ["userSettings": ARMRelationship(with: UserSettings.self, type: .hasOne)]
}
class UserSettings: ARMActiveRealm {
...
// The relation property. This property is just alias.
var author: Author {
return relations["author"]?.object as! Author
}
override class func definedRelationships() -> [String: ARMRelationship] {
return ["author": ARMInverseRelationship(with: Author.self, type: .belongsTo)]
}
}
CRUD
创建
save()
save
表示如果记录在数据库中不存在,则进行 INSERT 操作;如果存在,则执行 UPDATE。
// Initialize an instance.
let alice = Author()
alice.name = "Alice"
alice.age = 28
// Insert to Realm DB.
alice.save()
findOrInitialize(_:)
在 Realm DB 中查找对象,如果存在则返回。如果不存在,则以指定的参数初始化它,但还没有保存。
let author = Author.findOrInitialize(["name": "Bob", "age": 55])
author.save()
findOrCreate(_:)
在 Realm DB 中查找对象。如果存在,则初始化它并带有指定的参数,然后将其插入到数据库中。
let author = Author.findOrCreate(["name": "Bob", "age": 55])
读取
ActiveRealm 有许多读取操作。
all()
选择所有对象。
let authors = Author.all()
first()
选择首次创建的对象。
let tag = Tag.first()
first(limit:=)
从头部选择指定数量的对象。
let tags = Tag.first(limit: 10)
last()
选择最后一个创建的对象。
let tag = Tag.last()
last(limit:=)
从尾部选择指定数量的对象。
let tags = Tag.last(limit: 10)
find(ID:=)
通过指定的 ID 查找对象。
let author = Author.find(ID: "XXXXXXXX-XXXX-4XXX-XXXX-XXXXXXXXXXXX")
find(_:)
通过指定的参数查找对象。找到多个对象时,选择第一个对象。
let author = Author.find(["name": "Alice", "age": 28])
find(with:)
通过指定的参数查找对象。找到多个对象时,选择第一个对象。
let author = Author.find(with: NSPredicate(format: "age > %d", 28))
findLast(_:)
通过指定的参数查找对象。找到多个对象时,选择最后一个对象。
let tag = Tag.findLast(["articleID": "XXXXXXXX-XXXX-4XXX-XXXX-XXXXXXXXXXXX"])
findLast(with:)
通过指定的参数查找对象。找到多个对象时,选择最后一个对象。
let tag = Tag.findLast(with: NSPredicate(format: "articleID=%@", "XXXXXXXX-XXXX-4XXX-XXXX-XXXXXXXXXXXX"))
更新
保存()
let alice = Author.find(["name": "Alice"])
alice.age = 29
// Update.
alice.save()
删除
destroy()
destroy
方法默认执行级联删除。换句话说,相关数据也会一起删除。
alice.destroy()
destroy(_:)
根据指定的参数删除对象和相关对象。
Author.destroy(["name": "Alice"])
Author.destroy(with: NSPredicate(format: "name=%@", "Alice"))
destroyAll()
同时删除所有对象及其相关对象。
Author.destroyAll()
如果不执行级联删除,请使用以下方法。将 cascade
参数指定为 false。
alice.destroy(cascade: false)
Author.destroy(["name": "Alice"], cascade: false)
Author.destroy(with: NSPredicate(format: "name=%@", "Alice"), cascade: false)
Author.destroyAll(cascade: false)
Query
all
返回模型的所有对象。
let collection = Author.query.all
where(_:)
根据指定的参数返回对象。
let collection = Author.query.where(["name": "Alice"])
where(predicate:)
使用谓词返回指定搜索条件下的对象。
let collection = Author.query.where(predicate: NSPredicate(format: "age > %d", 40))
Collection
toArray
集合中的模型。转换为 NSArray。
let collection = Author.query.all
let authors = collection.toArray
count
集合中模型的数量。
let collection = Author.query.all
let count = collection.count
first
集合中的第一个模型。
let collection = Author.query.all
let author = collection.first
first(limit:)
返回头部的指定数量的模型。
let collection = Author.query.all
let authors = collection.first(limit: 5)
last
集合中的最后一个模型。
let collection = Author.query.all
let author = collection.last
last(limit:)
返回尾部的指定数量的模型。
let collection = Author.query.all
let authors = collection.last(limit: 5)
order(_:ascending:)
根据指定的属性对集合中的对象进行排序。
let collection = Author.query.all
collection.order("age", ascending: true)
at(_:)
根据给定索引获取对象。
let collection = Author.query.all
let author = collection.at(1)
pluck(_:)
提取模型的属性。
let collection = Author.query.all
let names = collection.pluck(["name"])
Count
计算对象的数量。
// The number of all objects.
let count = Author.count
// The number of objects matched given condition.
let count1 = Author.query.where(["name": "Alice"]).count
let count2 = Author.query.where(predicate: NSPredicate(format: "age > %d", 40)).count
被忽略的属性
ActiveRealm默认将模型中的所有属性保存到数据库中。如果您不希望保存某个属性,则可以重写ignoredProperties
方法。
class Author: ARMActiveRealm {
@objc var name = ""
@objc var age: NSNumber = 0
// A property ignored by ActiveRealm.
@objc var shortID: String {
return String(uid.split(separator: "-").first!)
}
override class func ignoredProperties() -> [String] {
return ["shortID"]
}
}
验证
ActiveRealm可以在保存模型之前验证数据。默认情况下,验证总是成功的。如果您想验证数据,可以重写validateBeforeSaving
方法。当validateBeforeSaving
方法返回false时,数据不会保存。
class Author: ARMActiveRealm {
@objc var name = ""
@objc var age: NSNumber = 0
override class func validateBeforeSaving(_ obj: Any) -> Bool {
let author = obj as! Author
// The name must not be empty.
return !author.name.isEmpty
}
}
转换方法
ActiveRealm可以轻松地将您模型中的属性转换为字典或JSON。
转换为字典
asDictionary()
class Author: ARMActiveRealm {
@objc var name = ""
@objc var age: NSNumber = 0
// A property ignored by ActiveRealm.
@objc var shortID: String {
return String(uid.split(separator: "-").first!)
}
override class func ignoredProperties() -> [String] {
return ["shortID"]
}
@objc func generation(_ obj: Author) -> NSNumber {
return NSNumber(integerLiteral: Int(age.doubleValue / 10.0) * 10)
}
}
let chris = Author.findOrCreate(["name": "Chris", "age": 32])
// Convert to a dictionary.
let dict = chris.asDictionary()
// => {
// age = 32;
// createdAt = "2019-06-07 07:45:05 +0000";
// name = Chris;
// uid = "D56F60E1-96C1-4083-A7D4-E216FF072DEA";
// updatedAt = "2019-06-07 07:45:05 +0000";
// }
asDictionary(excepted:)
使用属性名称的黑名单将内容转换为字典。
let dict = chris.asDictionary(excepted: ["uid", "createdAt", "updatedAt"])
// => {
// age = 32;
// name = Chris;
// }
asDictionary(included:)
使用属性名称的白名单将内容转换为字典。此方法还包括忽略的属性。
let dict = chris.asDictionary(included: ["name", "age", "shortID"])
// => {
// age = 32;
// name = Chris;
// shortUID = D56F60E1;
// }
asDictionary { prop, value in }
asDictionary(excepted:) { prop, value in }
asDictionary(included:) { prop, value in}
使用转换逻辑将数据转换为字典。
// Using a conversion logic.
let dict = chris.asDictionary(included: ["uid"]) { prop, value in
if prop == "uid" {
let uuid = value as! String
return uuid.split(separator: "-").first!
}
return value
}
// => {
// uid = D56F60E1;
// }
asDictionary(addingPropertiesWith:methods:)
asDictionary(excepted:addingPropertiesWith:methods:)
asDictionary(included:addingPropertiesWith:methods:)
将数据转换为字典,通过添加带有转换方法的目标属性。方法名称由Objective-C表示决定。
let dict = chris.asDictionary(included: ["name"],
addingPropertiesWith: chris,
methods: ["generation": "generation:"])
// => {
// generation = 30;
// name = Chris;
// }
转换为JSON
asJSON(), asJSONString()
asJSON方法返回Data类型值。另一方面,asJSONString方法返回String类型值。
class Author: ARMActiveRealm {
@objc var name = ""
@objc var age: NSNumber = 0
// A property ignored by ActiveRealm.
@objc var shortID: String {
return String(uid.split(separator: "-").first!)
}
override class func ignoredProperties() -> [String] {
return ["shortID"]
}
@objc func generation(_ obj: Author) -> NSNumber {
return NSNumber(integerLiteral: Int(age.doubleValue / 10.0) * 10)
}
}
let chris = Author.findOrCreate(["name": "Chris", "age": 32])
// Convert to a JSON.
let json = chris.asJSONString()
// => {
// "age" : 32,
// "uid" : "66703A0B-5712-4631-83C4-DF52E1CCE15F",
// "updatedAt" : "2019-06-07 09:15:15 +0000",
// "name" : "Chris",
// "createdAt" : "2019-06-07 09:15:15 +0000"
// }
asJSON(excepted:), asJSONString(excepted:)
使用属性名黑名单将对象转换为JSON。
let json = chris.asJSONString(excepted: ["uid", "createdAt", "updatedAt"])
// => {
// "age" : 32,
// "name" : "Chris"
// }
asJSON(included:), asJSONString(included:)
使用属性名白名单将对象转换为JSON。此方法还包含忽略的属性。
let json = chris.asJSONString(included: ["name", "age", "shortID"])
// => {
// "age" : 32,
// "name" : "Chris",
// "shortUID" : "66703A0B"
// }
asJSON { prop, value in }, asJSONString { prop, value in }
asJSON(excepted:) { prop, value in }, asJSONString(excepted:) { prop, value in }
asJSON(included:) { prop, value in }, asJSONString(included:) { prop, value in }
使用转换逻辑将对象转换为JSON。
let json = chris.asJSONString(included: ["uid"]) { prop, value in
if prop == "uid" {
let uuid = value as! String
return uuid.split(separator: "-").first!
}
return value
}
// => {
// "uid" : "66703A0B"
// }
asJSON(addingPropertiesWith:methods:), asJSONString(addingPropertiesWith:methods:)
asJSON(预期:添加属性和方法:), asJSONString(预期:添加属性和方法:)
asJSON(包含:添加属性和方法:), asJSONString(包含:添加属性和方法:)
将对象转换为JSON,通过一个转换方法添加属性。方法名称由Objective-C表示法指定。
let json = chris.asJSONString(excepted: ["uid", "createdAt", "updatedAt", "age"],
addingPropertiesWith: chris,
methods: ["generation": "generation:"])
// => {
// "generation" : 30,
// "name" : "Chris"
// }
Objective-C 中使用
Objective-C中使用,参见 我的示例代码。
待办事项
- 支持多对多关系
- 支持非空约束
- 支持原始类型。例如: NSInteger, float, double, BOOL 等...
- 属性名称映射