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

Mastering Realm Notifications

3a0ae72b2f6bdc4476f1fcb63396e717?s=47 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.

3a0ae72b2f6bdc4476f1fcb63396e717?s=128

JP Simard

December 08, 2016
Tweet

Transcript

  1. Mastering Realm Notifications JP Simard, @simjp, realm.io 1

  2. 2

  3. Cocoa Messaging 3

  4. 4

  5. Reactive Programming 5

  6. Reactive != Functional 6

  7. 7

  8. Reactive Programming just means reacting to changes & minimizing the

    explicit messaging about changes. 8
  9. It's about avoiding stale data 9

  10. It's about avoiding duplicate code paths 10

  11. Avoid different code paths —network —database —user action —push notification

    —system alerts —other processes 11
  12. 12

  13. 13

  14. Intro to Realm Notifications 14

  15. RealmTasks 15

  16. Types of Realm Notifications —Realm Notifications —Collection Notifications —KVO —Upcoming

    APIs... 16
  17. Realm Notifications 17

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

    realm.addNotificationBlock { notification, realm in viewController.updateUI() } // Later... token.stop() 18
  19. 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
  20. 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
  21. Collection Notifications 21

  22. Collection Notifications let realm = try Realm() let results =

    realm.objects(Item.self) // Auto-Updating Results let token = results.addNotificationBlock(tableView.applyChanges) 22
  23. 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
  24. Notifications may be coalesced! (grouped) 24

  25. 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
  26. Synchronous vs Asynchronous Queries Results are queried synchronously if requested,

    asynchronously otherwise. 26
  27. 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
  28. 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
  29. 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
  30. Interface-Driven Writes 30

  31. 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
  32. Like reordering rows. 32

  33. 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
  34. 34

  35. Key-Value Observation 35

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

    —ReactiveCocoa —Synchronously delivered —Single property at a time 36
  37. Objects as Messages 37

  38. 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
  39. Not limited to single-device —Also for Realm Object Server communication

    —And massively distributed apps 39
  40. 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
  41. 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
  42. Object-Level Notifications 42

  43. 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
  44. 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
  45. Object-Level Notifications —Coming early 2017 —Asynchronously delivered —Great for Objects-as-APIs/Messages

    and event handling 45
  46. 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
  47. Thank You! Questions? JP Simard, @simjp, realm.io 47