Realm Objective-C & Swift 2.1 – Improved Collection Notifications!

We’re releasing version 2.1 of Realm Objective‑C and Realm Swift today, which includes several improvements and bug fixes to fine-grained collection notifications. Read on to learn more!

Background (on) Notifications

The Realm SDKs have always provided a reactive, Objects-as-APIs approach to dealing with data, and with the recent launch of the Realm Mobile Platform, apps built on Realm can now easily respond to changes no matter where they originate from: other threads, processes and even other devices from half-way around the world 🌏!

With collection notifications, change information is continuously processed and delivered as operations representing what was inserted, deleted, moved and changed. Unlike ORMs that rely on diffing the old and new values, Realm’s change notifications leverage the semantic intent of operations to produce a much more accurate result, leading to nicer animations and easier code.

These notifications are always delivered asynchronously so your app never stutters or block the main UI thread. However, there are situations when changes need to be done synchronously, on the main thread, and reflected in the UI instantly. We refer to these transactions as interface-driven writes.

Interface-Driven Writes

We’ll refer to writes done on the UI thread and explicitly mirrored in the UI’s state as Interface-Driven Writes.

For example, say a user adds an item to a table view. The UI should ideally animate this operation and start this process as soon as the user initiates the action.

However, when the Realm change notification for this insertion is delivered a little later, it will indicate that an object was added to the collection backing the table view and we will once again attempt to insert a new row in the UI. This double insertion leads to inconsistent state between the UI and the backing data, which in turn will crash your app! 💥NSInternalInconsistencyException💥

Writes Skipping Notifications

To resolve this, we’re introducing a mechanism that allows for write transactions that are immediately reflected in the UI to avoid triggering a change notification for the changes that occurred during that transaction.

We’re calling these types of writes “writes skipping notifications”.

This feature especially useful when using fine-grained collection notifications with a synchronized Realm, because many of the workarounds to previously account for interface-driven writes rely on controlling the full state of when the app can perform changes. With synchronized Realms, changes are applied whenever they’re synchronized, which can happen at any point in the app’s lifetime.

Code Sample

// Add fine-grained notification block
self.notificationToken = self.collection.addNotificationBlock { [unowned self] changes in
  switch changes {
  case .Initial:
    self.tableView.reloadData()
  case .Update(_, let deletions, let insertions, let modifications):
    // Query results have changed, so apply them to the UITableView
    self.tableView.beginUpdates()
    self.tableView.insertRowsAtIndexPaths(insertions.map { NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .Automatic)
    self.tableView.deleteRowsAtIndexPaths(deletions.map { NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .Automatic)
    self.tableView.reloadRowsAtIndexPaths(modifications.map { NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .None)
    self.tableView.endUpdates()
  case .Error(let error):
    // handle error
  }
}

func insertItem() throws {
  // Perform an interface-driven write on the main thread:
  self.collection.realm!.beginWrite()
  self.collection.insert(Item(), atIndex: 0)
  // And mirror it instantly in the UI
  self.tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .Automatic)
  // Making sure the change notification doesn't apply the change a second time
  try self.collection.realm!.commitWrite(withoutNotifying: [self.notificationToken])
}

Demo

RealmTasks Fine-Grained Notifications

Thanks to RealmTasks being open-source, the pull requests adding fine-grained notifications and writes skipping notifications can be viewed on GitHub: iOS PR #352 and macOS PR #355.

Bug Fixes

As we were building this functionality, we took the opportunity to fix a number of issues with collection notifications, so this release should make them more reliable than ever before!

  • Deliver collection notifications when beginning a write transaction which advances the read version of a Realm (previously only Realm-level notifications were sent).
  • Fix some scenarios which would lead to inconsistent states when using collection notifications.
  • Fix several race conditions in the notification functionality.
  • Don’t send Realm change notifications when canceling a write transaction.

Legacy Swift Version Support

We’d like to remind you that we will continue to support Xcode 7.3.1 and Swift 2.2 as long as we can, but encourage all our users to migrate to Xcode 8 as soon as possible.


Thanks for reading. Now go forth and build amazing apps with Realm! As always, we’re around on Stack Overflow, GitHub, or Twitter.