Realm Notifications (RealmSwift Part 2)
This is the continuation of the previous part . If you know the basics of CRUD operation in Realm you can continue this.
As we saw in previous part live results in realm. Realm result sets always return the latest up-to-date data. Data in Results is never outdated. This means you never have to reload results from disk or somehow manually refresh in- memory data.
Even if you always access the latest up-to-date data, you still need to know when that data changes so you can update your app’s UI accordingly. In short we don’t have any mechanisms to know that data has changed so that we can update our UI.
To solve this problem Realm has a notification mechanism that lets you notify about when your data changes, and more importantly, it gives you precise fine-grained information about what was changed.
Since your UI has one source of truth from where data is coming and when new data arrives it needs to update accordingly. Without notification we usually have a reference to our UI in persistent layer and when data changes we update the UI from persistence layer which creates strong coupling between the UI and the Persistent layer. With notification UI reacting to changes that is happening in a reactive way. Let say we have some UI whose data source is array of
User objects so with realm notification we observe array of User and when data changed data source broadcasts notification to all observer (irrespective of thread and process) that data has changed and the one who is listening this notification will react accordingly.
Types of Notification
Realm provides notifications on three levels
Object Level → Observe Realm object for any property change
Collection Level → Observe Realm collections which includes List, Results and linking object properties
Realm Level → Observe changes in whole realm file
Note: Realm uses Apple’s run loops to deliver change notifications.
Let's dive into the coding part. First download the starter project . On the starter project we used previous part models with simplified version by removing some of the properties. You can see the model properties by looking into the User, Passport and Todo files.
Go to the
Viewcontroller we declare
users property which is auto-updating container type in Realm returned from object queries which holds the array of User realm model and it’s linking object. We will observe this property using Realm Notification feature. As shown in Figure 1
notificationToken is another property. When we Subscribed for notifications it returns a token object. You will get the notification until the lifetime of this token and if your token is released your notification will automatically stop since we need the notification until the lifetime of the controller we make this as a controller property to hold a strong reference to it. You can manually stop the notification by calling
invalidate() on the token object.
Run the application and as shown in Figure 2 we inserted User object into the database. Since Realm Result type always return a latest data
users now contain newly inserted object.
users added new object politely without telling anyone. By adding notification observer into the
users we can broadcast any changes in the
As shown in Figure 3 we added observers on users Realm Result type collection using observe method on collection. You pass it a closure, and it returns a notification token that you can use to control the notification subscription’s lifecycle. The closure parameter is of type
RealmCollectionChange which is an enum having three cases.
A RealmCollectionChange value encapsulates information about changes to collections that are reported by Realm notifications.
Delete the application and run again as shown in figure 4
RealmCollectionChange enum initial case execute. This method only call once when you initially observe a collection. This is good place to initialize your UI initial state with the initial data.
As shown in figure 5 when you run the application this screen will show. Section header contains user name on left and passport information on right whereas the rows in the section displays the todos of the user
Run the application and tap on
User model has
One-To Many relationship with the
Todo model . As shown in Figure 6 we added new todo to our user object. Note: All updation will happen on write block If you update the persisted realm object outside of this block you will encounter the crash as discussed in previous part as well.
As shown in Figure 7 since we are observing
users which is storing collection of
User model of Realm type and we update one of the property of this model which is
todos. Since in this application we only one User model which is populating UI at index 0 .
As printed on the console it is showing that at index 0 something has changed.
.update(collection, deletions, insertions, updates) is a notification triggered by any changes in the collection. The last three parameters are of type [Int] and represent the indexes in the collection that have been deleted, inserted, or updated. On this method we reload tableview to update the UI as we are sure that our
users will have the real time data.
Download the project with title “RealmTodoApplicationNotificationFinal” As shown in Figure 8 we are now observing
todos property of user object. To make thing simple we only expect one User Object in the database. This property has a To-Many relationship with the User model.
todos is Realm
List Type and we can observe it as we did with the
Result type. Also we are updating particular rows instead of the whole tableView. Note: Remove the self.tableView.reloadData() on update enum case
As shown in Figure 9 since we added 4
todos in our
User object . It is saying that newly Todo inserted on index 3 and we take this information to insert row at index 3 instead of reloading whole tableview.
Now tap on Update todo button it will update the User first todo object as as shown in Figure 10. Note: Remove the self.tableView.reloadData() on update enum case
Now tap on Delete todo button it will delete the User first todo object as as shown in Figure 11. Note: Remove the self.tableView.reloadData() on update enum case
Object Level Notification
We will use Object Level notification observer to observe changes on a single Realm object . In this example we added property observe to observe passport realm object which has a To-One relationship with the User Object.
The provided closure is the notification handler; Realm calls the closure every time a change occurs for a specific object. The change closure argument includes detailed information about what properties changed and in what way.
change(properties):When a property value is modified. properties is of type [PropertyChange] and provides information about which properties changed, along with the changed values, old and new.
deleted:Emitted once the object is deleted and you can no longer observe it.
Now tap on Update passport button it will update the passport number in the UI since we are reload whole table view. Other than there are things to observe
- We are observing Passport object on
main threadand we updated passport information on
background thread. You’ll be notified about changes made to your data from any of your app’s threads, or even other processes. The notification handler is called on the same thread where you subscribed for the notifications. Since we subscribed on main thread we are getting handler on
- Object level observer gives the fine-grained change information as shown in Figure 13 it gives the information about the property name , it’s old and new value. ‘
Note : old value become nil if observer and where the change happened on the same thread. As documented “ Value of the property before the change occurred. This is not supplied if the change happened on the same thread as the notification and for `List`properties.”
Realm Notification on Background Thread
To add a notification on a background thread you have to manually run a run loop on that thread and add the notification from within a callout from that run loop: GCD does not use a run loop on its worker threads, so anything based on dispatching blocks to the current thread’s run loop (such as Realm’s notifications) will never get called.See this link