RxSwift_v1.0笔记——21 RxRealm

RxSwift_v1.0笔记——21 RxRealm

很久以前,在一个遥远的平行宇宙,开发者开发app需要一个数据库,他们可以选择普遍但是啰嗦的Core Data,或者为SQLite创建自定义封装。然后Realm出现了,在应用程序中突然使用数据库变得轻而易举。

Realm中的数据库查询是“live”:结果集合的内容与任何数据库更改一起更新。 插入,修改或删除项目,您将立即看到“结果”对象中的更改。 此外,细粒度(fine-grained)通知可以提供有关Results对象中更改的详细信息。

List类型也是如此,它允许您构建对象集合,甚至在正在使用的单个对象也会自动更新其内容,同时应该对数据库进行更改。所有这一切使得Realm成为将这些动态变化暴露为observable序列的理想候选者!

RxRealm, https://github.com/RxSwiftCommunity/RxRealm ,是RxSwiftCommunity组织下的另一个项目。它的目标是帮助你无缝的集成Realm到你的响应式流程。

Auto-updating results 343

数据库查询定义了一个对象的集合。在最基本的层面上,RxRealm封装结果为一个observable,将其与初始内容一起触发,然后每次集合更改时再次发生:

let realm = try! Realm()
let result = realm.objects(MyObject.self)
Observable.collection(from: result)
  .subscribe(onNext: { items in
    print("Query returned \(items.count) items")
  })
  .addDisposableTo(disposeBag)

每次你提交一个改变到数据库,它将影响结果,集合将再一次触发

Arrays 344

获得一个数组替代Realm集合是简单的:

let result = realm.objects(MyObject.self)
Observable.array(from: result)
  .subscribe(onNext: { array in
    print("Query returned \(array.count) items")
  })
  .addDisposableTo(disposeBag)
Note:因为来至结果的每个对象必须从数据库加载,有一个记忆和时间惩罚来抓取所有的对象作为一个数组。与collection(from:)工作通常是你最需的。

Asynchronous first item 344

Observable.collection(from:) and Observable.array(from:)会同步发射初始项目的集合或数组。在这时你订阅到observable( subscribe(:)函数返回前),你接收了第一个元素。这会干扰你的应用,尤其是当启动用户界面时。一个常见的错误来源是,假设下面的代码已经被执行了 - 但是没有为第一个元素写 subscribe(_:)。

幸运的是,两个函数实际上接收第二个参数,它默认是true。实际的签名是 Observable.collection(from:synchronousStart:) and Observable.array(from:synchronousStart:)。

如果你想让第一个元素异步发射,为异步标志位传递false:

let result = realm.objects(MyObject.self)
Observable.array(from: result, synchronousStart: false)
  .subscribe(onNext: { array in
    print("Query returned \(array.count) items")
  })
  .addDisposableTo(disposeBag)

当初始Realm通知触发时,你将接收初始内容。

变更集(Changesets) 345

RxRealm能够访问更多复杂的observables。你能够获得changesets(当结果更新时Realm通知)使用 Observable.changeset(from:synchronizedStart:)。这个observable同原结果的集合一起发送changeset

let result = realm.objects(MyObject.self)
Observable.changeset(from: result)
  .subscribe(onNext: { results, changes in
  if let changes = changes {
      // it's an update
      print("deleted: \(changes.deleted)")
      print("inserted: \(changes.inserted)")
      print("updated: \(changes.updated)")
    } else {
      // it's the initial data
    print(results)
    }
  }
  .addDisposableTo(disposeBag)

Observable.arrayWithChangeset(from:synchronizedStart:)是这个API的一个变体,它返回一个数组的observable和一个Realm ChangeSet。

Single objects 345

RxRealm的最终特性让你观察单个对象并在数据库中它每次更新就获得一个新的。例如,如果你在数据库中的对象里存储用户设置,在你代码中变化的位置观察它来自动更新设置变化。

如果你正在使用Realm移动平台,为来自其他设备的活动更新尤为简单:

let hideCompletedObs = Observable.from(object: preferencesObject)
  .map { prefs -> Bool in prefs.hideCompletedTodos }
  .distinctUntilChanged()
let todosObs = realm.objects(Todo.self)
Observable.combineLatest(hideCompletedObs, todosObs) {
  hideCompleted, todos in
  if hideCompleted {
    return todos.filter { $0.completed == false }
  }
  return todos
  }
  .bindTo(tableView.rx.items) {
    (tableView: UITableView, index: Int, element: Todo) in
    // return cell here
  }
  .addDisposableTo(disposeBag)

在这种情况下,您的应用程序将显示一个待办事项列表,该列表最初会刷新一次,然后再次在用户首选项中“hide completed todos”标志更改。

如果(in case)您只对对象的特定属性的变化感兴趣,则可以使用Observable .propertyChanges(object:)来订阅每个属性的改变事件。在上面的todo应用场景中,你可能想观察一个给定任务的标题的改变,如下示:

Observable.propertyChanges(object: task)
  .filter { $0.name == "taskTitle" }
  .subscribe(onNext: { change in
    print("property \(change.name) changed from: \(change.oldValue) to: \
      (change.newValue)")
  })
  .addDisposableTo(bag)

Adding objects 346

RxRealm用 add() and delete() 观察者扩展了Realm类。你能够使用它们来观察项目增或删的序列。每次序列发射一个对象,它能够自动地从数据库增加或删除。

考虑这个情景,你需要在特定间隔为新消息查询远端服务器。当API返回新消息,你希望看到消息增加到数据库(你在你应用的某处使用RxRealm来自动更新数据库更改的消息列表):

Observable
  .timer(0, period: 60, scheduler: MainScheduler.instance)
  .flatMap { _ -> Observable<[Message]> in
    let messages = realm.objects(Messages.self).sorted(byProperty:
      "dateReceived")
    if let lastMessage = messages.last {
      return MailAPI.newMessagesSince(lastMessage.dateReceived)
    }
    return MailAPI.newMessagesSince(Date.distantPast)
  }
  .subscribe(realm.rx.add())
  .addDisposableTo(disposeBag)

简单地忽略由定时器发射的值,请求自最后接收消息的日期之后的所有信息并传递结果到RxRealm。

当你在一个realm对象上使用add()是要小心。RxRealm为订阅的持续保持了Realm数据库对象。如果你不希望订阅保持Realm对象,你可以使用它自己的变化版: Realm.rx.add() or Realm.rx.add(configuration:)——这个静态方法将抓取在当前线程的任何存在的Realm实体,并且不需要保持的使用它。

Deleting objects 347

类似的端点(endpoint)让你删除来自数据库的存在的对象或对象的集合:

let realm = try! Realm()
deleteSelectedMessagesButton.rx.tap
  .map { self.selectedMessages() }
  .bindTo(realm.rx.delete())
  .addDisposableTo(disposeBag)

再次,在订阅的整个持续时间内保持了Realm对象。

推荐阅读更多精彩内容