We first looked at Realm way back in Bite #49. It's a great data storage solution for our mobile apps. Today we'll start looking at some of the latest improvements in Realm and the new capabilities they offer. First up is Fine-grained notifications. Let's dive in:

Realm has offered notifications of write operations for a while, they look like this:

let token = realm.addNotificationBlock { notif, realm in
  // TODO: viewController.updateUI()
}

These are still around and work great, but it might help to know more about what changed. That's where the new Collection Notifications come in.

Collection notifications give us access the changes that just occurred at a fine-grained level, including the specific indices of insertions, deletions, etc

let results = try! Realm().objects(Spaceship).sorted("name")
let token = results.addNotificationBlock { (changes: RealmCollectionChange) in
  // TODO: self.processChanges(changes)
}

changes here is an enum that looks like this:

public enum RealmCollectionChange<T> {
  case Initial(T)
  case Update(T, deletions: [Int], insertions: [Int], modifications: [Int])
  case Error(NSError)
}

.Update's values can be easily mapped to NSIndexPath objects suitable for use in table views and collection views.

Here's a complete example showing all of this in action:

class SpaceshipsViewController: UITableViewController {
  var notificationToken: NotificationToken? = nil

  override func viewDidLoad() {
    super.viewDidLoad()
    let realm = try! Realm()
    let results = realm.objects(Spaceships).filter("maxSpeed > 0")

    // Observe Results Notifications
    notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
      guard let tableView = self?.tableView else { return }
      switch changes {
      case .Initial:
        // Results are now populated and can be accessed without blocking the UI
        tableView.reloadData()
        break
      case .Update(_, let deletions, let insertions, let modifications):
        // Query results have changed, so apply them to the UITableView
        tableView.beginUpdates()
        tableView.insertRowsAtIndexPaths(insertions.map { NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .Automatic)
        tableView.deleteRowsAtIndexPaths(deletions.map { NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .Automatic)
        tableView.reloadRowsAtIndexPaths(modifications.map { NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .Automatic)
        tableView.endUpdates()
        break
      case .Error(let error):
        // An error occurred while opening the Realm file on the background worker thread
        fatalError("\(error)")
        break
      }
    }
  }

  deinit {
    notificationToken?.stop()
  }
}

More info about Realm can be found at realm.io