实时 0.9.7

Realtime 0.9.7

Denis Koryttsev维护。



Realtime 0.9.7

Realtime

Version License Platform

Realtime是一个ORM框架,它使得创建复杂数据库结构变得简单。

功能

👉 简单可扩展的模型结构

👉 文件

👉 集合

👉 参考

👉 UI, 表单

支持Firebase数据库

Firebase Realtime Database完全支持并投放到生产环境中。如果您使用干净的Firebase API,Realtime可以帮助您更快地创建应用程序,并应用于存储复杂数据结构、使用响应性行为更新UI。Realtime提供轻量级数据流量、数据懒加载初始化、良好的数据分布。

支持FoundationDB

FoundationDB受支持,但有一些限制,因为FDB没有原生的观察机制。

用法

初始化

AppDelegate 中的 func application(_:didFinishLaunchingWithOptions:) 方法里,你必须调用下面的代码来配置工作环境。目前,缓存策略的有效值仅为 .noCache.persistance。内存缓存尚未实现。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    /// ...

    /// initialize Realtime
    RealtimeApp.initialize(...)

    ///...
    return true
}

模型

要创建任何模型数据结构,你可以通过继承 Object 来实现。你可以使用类定义子属性。

  • Object 子类;
  • ReadonlyPropertyPropertyReferenceRelationReadonlyFileFile
  • ReferencesValuesAssociatedValues 等;如果你使用懒惰属性,你需要实现类函数 lazyPropertyKeyPath(for:)。如果您知道如何在不继承 NSObject 的情况下避免它,请告诉我。(请告诉我,如果不继承 NSObject 也能避免它,应该如何做。)这个函数将对每个子类被调用一次,因此您不需要调用超类的实现。示例
class User: Object {
    lazy var name: Property<String> = "name".property(in: self)
    lazy var age: Property<Int> = "age".property(in: self)
    lazy var photo: File<UIImage?> = "photo".file(in: self, representer: .png)
    lazy var groups: References<RealtimeGroup> = "groups".references(in: self, elements: .groups)
    lazy var scheduledConversations: Values<Conversation> = "scheduledConversations".values(in: self)
    lazy var ownedGroup: Relation<RealtimeGroup?> = "ownedGroup".relation(in: self, "manager")

    override class func lazyPropertyKeyPath(for label: String) -> AnyKeyPath? {
        switch label {
        case "name": return \User.name
        case "age": return \User.age
        case "photo": return \User.photo
        case "groups": return \User.groups
        case "ownedGroup": return \User.ownedGroup
        case "scheduledConversations": return \User.scheduledConversations
        default: return nil
        }
    }
}

let user = User(in: Node(key: "user_1"))
user.name <== "User name"
user.photo <== UIImage(named: "img")

let transaction = user.save(in: .root)
transaction.commit(with: { state, err in
    /// process error
})

属性

ReadonlyProperty - 用于任何值的只读存储属性。

Property - 用于任何值的存储属性。

SharedProperty - 与 Property 类似的存储属性,但使用并发事务来更新值。如果值具有共享访问权限(例如 '点赞数' 值),请使用此属性。

引用

Reference - 存储对任何数据库值的引用。不保证引用完整性。如果记录不会被删除或其他不需要引用完整性的原因,请使用它。

Relation - 存储对任何数据库值的引用。它在相关对象的一侧创建链接。在删除相关对象时,将删除引用。

文件

ReadonlyFile - Firebase Storage中的文件只读存储属性。

File - Firebase Storage中的文件存储属性。

集合

class Some: Object {
    lazy var array: Values<Object> = "some_array".values(in: self)
    lazy var references: References<Object> = "some_linked_array".references(in: self, elements: .linkedObjects)
    lazy var dictionary: AssociatedValues<Object> = "some_dictionary".dictionary(in: self, keys: .keyObjects)
}

某些集合的可变操作可能需要 isSynced 状态。要达到此状态,使用 func runObserving() 函数或将属性 keepSynced: Bool 设置为 true

References 是存储对象的引用的数组。源元素必须在同一个引用中。在将对象插入到该数组中时,在对象旁边创建链接。

Values 是存储对象本身的值的数组。

ReferencesValues 可变

do {
    let transaction = Transaction()
    ...
    let element = Element()
    try array.write(element: element, in: transaction)
    try otherArray.remove(at: 1, in: trasaction)

    transaction.commit { (err) in
        // process error

        self.tableView.reloadData()
    }
} catch let e {
    // process error
}

AssociatedValues 是键为引用,但值为对象的字典。在保存值时,在键对象旁边创建链接。

AssociatedValues 可变

do {
    let transaction = Transaction()
    ...
    let element = Element()
    try dictionary.write(element: element, key: key, in: transaction)
    try otherDictionary.remove(by: key, in: transaction)

    transaction.commit { (err) in
        // process error
    }
} catch let e {
    // process error
}

MapRealtimeCollection 是不可变集合,它从映射函数中获取元素。这是 x.lazyMap(_ transform:) 方法的返回结果,其中 x 是任何实时集合。

let userNames = Values<User>(in: usersNode).lazyMap { user in
    return user.name
}

运算符

  • <== - 赋值运算符。可以用来将值赋给(或从)任何实时属性。
  • ====!=== - 比较运算符。可以用来比较任何实时属性的值符合 Equatable 协议。
  • ?? - 中缀运算符,执行空合并运算,返回实时属性的包装值或默认值。
  • <- - 前缀运算符。可以用来将 Closure, Assign 类型的实例转换为显式闭包或反向转换。

事务

Transaction - 包含所有写入事务信息的对象。几乎所有的数据更改都是通过此对象执行的。最可变的操作只需将事务作为参数即可,但要创建自定义复杂操作,您可以使用这些方法

/// adds operation of save RealtimeValue as single value as is
func set<T: RealtimeValue & RealtimeValueEvents>(_ value: T, by node: Node)
/// adds operation of delete RealtimeValue
func delete<T: RealtimeValue & RealtimeValueEvents>(_ value: T)
/// adds operation of update RealtimeValue
func update<T: ChangeableRealtimeValue & RealtimeValueEvents & Reverting>(_ value: T)
/// method to merge actions of other transaction
func merge(_ other: Transaction)

更多详细信息请参阅示例项目。

UI

SingleSectionTableViewDelegate - 为UITableView提供带有自动更新的单节数据源。SectionedTableViewDelegate - 为UITableView提供带有自动更新的分节数据源。CollectionViewDelegate - 为UICollectionView提供带有自动更新的数据源。

delegate.register(UITableViewCell.self) { (item, cell, user, ip) in
    item.bind(
        user.name, { cell, name in
            cell.textLabel?.text = name 
        }, 
        { err in
            print(err)
        }
    )
}
delegate.bind(tableView)
delegate.tableDelegate = self

// data
users.changes
    .listening(
        onValue: { [weak tableView] (e) in
            guard let tv = tableView else { return }
            switch e {
            case .initial: tv.reloadData()
            case .updated(let deleted, let inserted, let modified, let moved):
                tv.beginUpdates()
                tv.insertRows(at: inserted.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
                tv.deleteRows(at: deleted.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
                tv.reloadRows(at: modified.map({ IndexPath(row: $0, section: 0) }), with: .automatic)
                moved.forEach { from, to in
                    tv.moveRow(at: IndexPath(row: from, section: 0), to: IndexPath(row: to, section: 0))
                }
                tv.endUpdates()
            }
        },
        onError: onError
    )
    .add(to: listeningCollector)

表单

class User: Object {
    var name: Property<String>
    var age: Property<Int>
}

class FormViewController: UIViewController {
    var form: Form<User>

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let name = Row<TextCell, Model>.inputRow(
            "input",
            title: Localized.name,
            keyboard: .name,
            placeholder: .inputPlaceholder(Localized.name),
            onText: { $0.name <== $1 }
        )
        name.onUpdate { (args, row) in
            args.view.textField.text <== args.model.name
        }
        let age = Row<TextCell, Model>.inputRow(
            "input",
            title: Localized.age,
            keyboard: .numberPad,
            placeholder: requiredPlaceholder,
            onText: { $0.age <== $1 }
        )
        age.onUpdate { (args, row) in
            args.view.textField.text <== args.model.age
        }
        let button: Row<ButtonCell, Model> = Row(reuseIdentifier: "button")
        button.onUpdate { (args, row) in
            args.view.titleLabel.text = Localized.login
        }
        button.onSelect { [unowned self] (_, row) in
            self.submit()
        }

        let fieldsSection: StaticSection<Model> = StaticSection(headerTitle: nil, footerTitle: nil)
        fieldsSection.addRow(name)
        fieldsSection.addRow(age)

        let buttonSection: StaticSection<Model> = StaticSection(headerTitle: nil, footerTitle: nil)
        buttonSection.addRow(button)

        form = Form(model: User(), sections: [fieldsSection, buttonSection])
        form.tableView = tableView
        form.tableDelegate = self
    }
}

本地监听

要接收本地级别的变更,请使用响应此协议的对象。它具有类似的RxSwift接口。

public protocol Listenable {
    associatedtype OutData

    /// Disposable listening of value
    func listening(_ assign: Assign<OutData>) -> Disposable
}

调试

通过启动时传递调试参数 'REALTIME_CRASH_ON_ERROR',以捕获内部错误。

JS

还存在为Vue.js应用程序创建的NodeJS模块。源代码可以在 js 文件夹中找到。

局限性

实时对象不应在线程之间传递。

示例

要运行示例项目,首先克隆仓库,然后在 Example 目录中运行 pod install

要求

Xcode 9+,Swift 4.1+。

安装

SwiftPM

    .package(url: "https://github.com/k-o-d-e-n/realtime.git", .branch("master"))

Realtime 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中

pod 'Realtime'

作者

Koryttsev Denis, [email protected] Twitter: @K_o_D_e_N

许可

Realtime 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。