I'm working on an iOS app that uses a Realm database. In my AppDelegate
, I define a Realm result set like this:
var results: Results<RealmWidget>!
var notificationToken: NotificationToken? = nil
do {
let realm = try Realm()
if results == nil {
results = realm.objects(RealmWidget.self)
}
} catch {
print(error.localizedDescription)
}
I observe this result set for changes like this:
if notificationToken == nil {
notificationToken = results.observe { (changes: RealmCollectionChange) in
switch changes {
case .update(_, _, let insertions, _):
if insertions.count > 0 {
// show badge on tab bar
// play sound to get the user's attention
}
default:
break
}
}
}
Anytime a new RealmWidget
object is inserted in the database (which happens as a result of receiving data from a server), I want to show a badge on the UITabBar
in my UI. This code appears to be working properly.
In another part of my app, I have a view controller with a UITableView
that is used to display all of the RealmWidget
objects (sorted by a timestamp). Inside of that view controller, I also have a result set that I observe for changes so I know when to reload the tableview. That code looks like this:
var results: Results<RealmWidget>!
var notificationToken: NotificationToken? = nil
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
results = RealmHelper.getRealmWidgets() // helper method that queries Realm for all of the Widget objects and sorts them by timestamp
notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
DispatchQueue.main.async {
self!.tableView.reloadData()
}
}
}
}
This code also appeared to be working properly until I added code to play a sound into the observation block in my AppDelegate
. Then I realized that anytime I delete a row from the UITableView
in this view controller, I was also hearing the sound play. This was unexpected because I should only be adding the badge to the UITabBar
and playing the sound when there is an insertion, not a deletion.
I added some debug code into the observation block that's defined in my AppDelegate
so it looks like this:
if notificationToken == nil {
notificationToken = results.observe { (changes: RealmCollectionChange) in
switch changes {
case .update(_, let deletions, let insertions, let modifications):
print("widget deletion count: \(deletions.count)")
print("widget insertion count: \(insertions.count)")
print("widget modification count: \(modifications.count)")
if insertions.count > 0 {
// show badge on tab bar
// play sound to get the user's attention
}
default:
break
}
}
}
When I run the app, switch to the view controller with the tableview and then delete one of the rows (which causes the corresponding RealmWidget
object to be deleted from the database), I see the following output printed in the debug console:
widget deletion count: 2
widget insertion count: 1
widget modification count: 0
A couple of things about this are unclear to me:
If anyone can explain what's happening here I would certainly appreciate the help!
-- EDIT --
I added the same debug code into my observation block that is defined inside the view controller and noticed something else strange. When this "problem" occurs, I see this output in the debug console:
widget deletion count (AppDelegate): 2
widget insertion count (AppDelegate): 1
widget modification count (AppDelegate): 0
widget deletion count (View controller): 1
widget insertion count (View controller): 0
widget modification count (View controller): 0
So the observation block in my view controller shows 1 deletion but the observation block in my AppDelegate shows 2 deletions and 1 insertion! Oddly enough, this doesn't happen 100% of the time. If my UITableView
is showing 10 RealmWidget
objects and I delete them one at a time, about 75% of the time, I see the above output in the console. But the other 25% of the time, the output is exactly what I was expecting to see:
widget deletion count (AppDelegate): 1
widget insertion count (AppDelegate): 0
widget modification count (AppDelegate): 0
widget deletion count (View controller): 1
widget insertion count (View controller): 0
widget modification count (View controller): 0
1 deletion in both observation blocks. I'm even more confused now than I was before! ;-)
-- EDIT #2 --
It appears that this behavior is related to the fact that the Results<RealmWidget>
in my AppDelegate and the Results<RealmWidget>
in my view controller are not ordering the query results in the same way.
In my AppDelegate
, results
is an unsorted collection of all the RealmWidget
objects in the Realm. But in my view controller, the helper method that I use to define results
is a sorted collection of all the RealmWidgets
objects in the Realm.
I posted this question to Realm via GitHub and got the following response:
Yes, unsorted results are rearranged when objects are deleted. If you don't ask for an explicit sort order, then the objects are simply listed in the order that they happen to be stored on disk, and that is an unstable order that changes when objects are deleted.
We currently report the object at the end moving to the place of a deleted object as that object also being deleted and then inserted in the new location.
https://github.com/realm/realm-cocoa/issues/6130
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With