ActiveRealm 0.2.1

ActiveRealm 0.2.1

Hituzi Ando维护。



  • 作者:
  • Hituzi Ando

ActiveRealm

ActiveRealm是使用Realm的Objective-C/Swift Active Record库,灵感来源于Ruby on Rails的ActiveRecord。

日语doc

特性

  • Objective-C和Swift制作
  • 连接到Realm DB
  • 许多读取操作
  • 通过一对一或一对多关系级联删除
  • ActiveRealm对象可以在多线程中使用

安装

CocoaPods

ActiveRealm可通过CocoaPods获取。要安装它,只需将以下行添加到Podfile中:

pod "ActiveRealm"

手动安装

  1. 安装Realm
  2. 下载最新的ActiveRealm
  3. 将ActiveRealm.framework拖放到您的Xcode项目中

开始使用

  1. 导入

    import ActiveRealm
  2. 配置 Realm

    首先,将您的 Realm 配置为默认的 Realm。详细信息,请参阅这里

    // Configure your Realm configuration as default Realm.
    let configuration = RLMRealmConfiguration.default()
    // Something to do
    RLMRealmConfiguration.setDefault(configuration)
  3. 实现模型

    使用 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 等...
  • 属性名称映射