Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mastering Realm Notifications

JP Simard
December 08, 2016

Mastering Realm Notifications

Realm takes a reactive, Objects-as-APIs approach to dealing with data. In this talk, we'll go over tips and tricks for building a variety of user interactions by building off a few primitive Realm notification concepts. We'll cover Interface-Driven Writes, asynchronous vs synchronous query computation, how to architect your app to handle changes that can occur at arbitrary times and how all of this ties into the Realm Mobile Platform.

JP Simard

December 08, 2016
Tweet

More Decks by JP Simard

Other Decks in Programming

Transcript

  1. 2

  2. 4

  3. 7

  4. 12

  5. 13

  6. Realm Notifications let realm = try Realm() let token =

    realm.addNotificationBlock { notification, realm in viewController.updateUI() } // Later... token.stop() 18
  7. Realm Notifications —Use when you want to react to everything

    that happens —Asynchronously delivered —Realm.refresh() waits for these notifications to be delivered —There's usually a better way 19
  8. Notification Token —How Realm knows how long to deliver notifications

    for. —Must hold a strong reference. —Best to call stop() when you no longer want to be notified. —Logs "released without unregistering a notification" if deallocated without calling stop(). Harmless but the best way we know to inform 20
  9. Collection Notifications let realm = try Realm() let results =

    realm.objects(Item.self) // Auto-Updating Results let token = results.addNotificationBlock(tableView.applyChanges) 22
  10. Collection Notifications extension UITableView { func applyChanges<T>(changes: RealmCollectionChange<T>) { switch

    changes { case .initial: reloadData() case .update(let results, let deletions, let insertions, let updates): let fromRow = { (row: Int) in return IndexPath(row: row, section: 0) } beginUpdates() insertRows(at: insertions.map(fromRow), with: .automatic) reloadRows(at: updates.map(fromRow), with: .automatic) deleteRows(at: deletions.map(fromRow), with: .automatic) endUpdates() case .error(let error): fatalError("\(error)") } } } 23
  11. Notifications may be coalesced (grouped) switch changes { case .initial:

    reloadData() case .update(let results, let deletions, let insertions, let updates): let fromRow = { (row: Int) in return IndexPath(row: row, section: 0) } beginUpdates() insertRows(at: insertions.map(fromRow), with: .automatic) reloadRows(at: updates.map(fromRow), with: .automatic) deleteRows(at: deletions.map(fromRow), with: .automatic) endUpdates() case .error(let error): fatalError("\(error)") } 25
  12. Asynchronous Query let realm = try Realm() // Query never

    performed on the current thread let results = realm.objects(Item.self) let token = results.addNotificationBlock { _ in /* results available asynchronously here */ } 27
  13. Synchronous Query let realm = try Realm() // Query performed

    synchronously to return first result _ = results.first // <- query performed here let token = results.addNotificationBlock { _ in /* results available asynchronously here */ } 28
  14. Collection Notifications —May be coalesced —Generally delivered on runloop iterations

    —Query upkeep always performed on background thread —Notifications delivered on the source thread —Can avoid all source thread overhead if used correctly 29
  15. Interface-Driven Writes —Changeset notifications are all about applying state diffs.

    —State must be up to date for the diff to make sense. —Some changes should be applied to the UI synchronously. 31
  16. Interface-Driven Writes func viewDidLoad() { messages = realm.objects(Message.self) self.token =

    messages.addNotificationBlock(tableView.applyChanges) } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { guard editingStyle == .delete else { return } realm.beginWrite() messages.removeAtIndex(indexPath.row) tableView.deleteRows(at: [indexPath], with: .automatic) realm.commitWrite(withoutNotifying: [self.token]) } 33
  17. 34

  18. Key-Value Observation —Enables Realm to work well with Cocoa APIs

    —ReactiveCocoa —Synchronously delivered —Single property at a time 36
  19. Inter-process data sharing/message passing —Realm already has robust interprocess data

    sharing —Could use to share data or notify main iOS app from app extension 38
  20. Objects as Messages let managementRealm = try user.managementRealm() try managementRealm.write

    { let permissionChange = SyncPermissionChange(realmURL: realmURL, userID: anotherUserID, mayRead: true, mayWrite: true, mayManage: false) managementRealm.add(permissionChange) } 40
  21. Objects as Messages let collectionOfOne = managementRealm.objects(SyncPermissionChange.self) .filter("id = %@",

    permissionChange.id) token = collectionOfOne.addNotificationBlock { notification in if case .update(let changes, _, _, _) = notification, let change = changes.first { // Object Server processed the permission change operation switch change.status { case .notProcessed: break // handle case case .success: break // handle case case .error: break // handle case } print(change.statusMessage) // contains error or message } } 41
  22. Object-Level Notifications let realm = try Realm() let jane =

    realm.objects(Person.self).filter("name == 'Jane'").first! let token = jane.addNotificationBlock { change in switch change { case .change(let propertyChanges): for propChange in propertyChanges { print("'\(propChange.name)': \(propChange.oldValue) -> \(propChange.newValue)") } case .deleted: print("object was deleted") case .error(let error): print("notification couldn't be delivered: \(error)") } } // Later... token.stop() 43
  23. Object-Level Notifications let realm = try Realm() let state =

    realm.objects(AppState.self).first! let token1 = state.addNotificationBlock(observingProperties: ["unreadCount", "status"], block: { change in // handle change and restrict to properties we care about }) let token2 = state.addNotificationBlock(ignoringProperties: ["noisyProp"], block: { change in // handle change and avoid being notified for properties we don't care about }) 44
  24. Resources —Realm's docs on Notifications —realm.io/docs/swift#notifications —Live Objects and Fine-Grained

    Notifications —realm.io/news —RealmTasks —github.com/realm/RealmTasks —Removing workaround PR: RealmTasks#352 —RxRealm: RxSwiftCommunity/RxRealm 46