Realtime
Realtime是一个ORM框架,它使得创建复杂数据库结构变得简单。
功能
支持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
子类;ReadonlyProperty
,Property
,Reference
,Relation
,ReadonlyFile
,File
;References
,Values
,AssociatedValues
等;如果你使用懒惰属性,你需要实现类函数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 是存储对象本身的值的数组。
References
,Values
可变
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 文件。