Using realm 2.03.
I have been fighting realm notifications and uitableview updates for several months now...
My use case has a table with data split into sections. I have a single realm results with a notification block that splits the data across the sections and keeps track of how many of the results are in each section.
When I run a test that inserts (through separate write transactions), lots of elements, I see some very strange behavior and end up with uitableview crashes (mismatch of # of rows and insertions).
My question: does the RLMResults reflect all write transactions before the notification blocks are handled succesively? Is there notification coalescing?
If the RLMResults shows the updated data set immediately, how am I supposed to update a uitableview without calling reloadData?
Thanks!
We made a number of fixes to collection notifications in 2.1.0, and it's likely the issue you're seeing has been addressed. See our 2.1 release post for details: https://realm.io/news/realm-objc-swift-2.1/
Also recently released is 2.1.1, which includes even more fixes. I encourage you to update at your earliest convenience: https://github.com/realm/realm-cocoa/releases/tag/v2.1.1
In the future, we'd also appreciate if you could confirm an issue exists with the latest version of Realm before filing an issue. Thanks!
In our case the updates are in a background thread, not the UI thread. We will try updating and report back!
@jpsim how to update to the 2.1.1 version?
Using pod 'RealmSwift' uses the 2.1.0 version
@danipralea run pod repo update and specify pod 'RealmSwift', '= 2.1.1' in your Podfile.
@asjesset were you able to update and check to see if your issue is resolved? Thanks!
@asjesset The way you deal with this is putting the updates into a serial queue (including updating the row #). Only move to the next item in the queue when the table has completed updating.
You should just be updating the table view from the main thread. UITableView APIs aren't designed to be changed on a background thread. If you follow that, then it's by definition serial since it's being performed on the same (main) thread.
@jpsim Sure that is how it should work in theory, but in practice things are different. When you are doing quick successive updates, things look a bit different. Obviously you need to update the UITableView from the main thread -- it crashes otherwise. But when there are animations involved (which due to the bugginess of UITableView are hard to prevent), you can get multiple overlapping updates where it reads the wrong value for the # of rows and gets into an inconsistent state. So I have to put updates and most importantly changing the value of the overall # of rows value into a different serial queue and use a semaphore to block until the animation is complete. Obviously this isn't Realm's fault -- it's that Apple's UI stuff isn't properly adapted to multi-core environments yet.
My question: does the RLMResults reflect all write transactions before the notification blocks are handled succesively? Is there notification coalescing?
If the RLMResults shows the updated data set immediately, how am I supposed to update a uitableview without calling reloadData?
RLMResults has been updated when the notification block are called. So what you should do is just call reloadData() to reflect state of the model to UI, or insert/delete/reload RowsAtIndexPath() methods according to the RealmCollectionChange to reflect state of the model to UI with animations.
Could you please let me know the issue happens again even if updating Realm to 2.1.1, thanks @asjesset
This should have been resolved in 2.1.1; change notifications were updated to be more robust when updating the state of UITableView controls.
@asjesset - If updating to Realm 2.1.1 hasn't solved the issue for you, please feel free to re-open this issue.
Happy holidays!
I am not seeing this issue anymore. It's pretty challenging to determine though! Will post back if I see it again.
Hi i m facing same issue using RealmSwift (2.3.0):
I have used realmcollection notification .But got crashed while setting a filter query
tasks = tasks.filter("selfAssignment = %@","true")
notification code are as follows
func notificationSubscription(tasks: Results
return tasks.addNotificationBlock {[weak self] (changes: RealmCollectionChange
self?.updateUI(changes: changes)
}
}
func updateUI(changes: RealmCollectionChange<Results<RMCAssignmentObject>>) {
switch changes {
case .initial(_):
assignmentTableView.reloadData()
case .update(_, let deletions, let insertions, _):
assignmentTableView.beginUpdates()
assignmentTableView.insertRows(at: insertions.map {IndexPath(row: $0, section: 0)},
with: .automatic)
assignmentTableView.deleteRows(at: deletions.map {IndexPath(row: $0, section: 0)},
with: .automatic)
**# assignmentTableView.endUpdates()** . -- **crashed here**
break
case .error(let error):
print(error)
}
}
Pls provide any solution
Hi @Raghvendra7. Please file another issue as from my understanding, these issues could be related but I don't believe they're the same issue. Feel free to reference this issue as well. Thanks!
This issue is definitely not solved.
We are trying to adopt Realm notifications (from our previous custom mechanism) and are observing this same issue (tried in both Realm v2.5.1 and 2.7.0).
Logic seems very straight-forward:
override func viewDidLoad() {
// load data
chats = try! Realm().objects( Chat.self )
// show only ongoing chats
.filter( "SUBQUERY(memberStates, $memberState, $memberState.member.DAL_id == '\(User.currentUser.person.id.stringValue)' && $memberState.isArchived == false).@count > 0" )
.sorted( byKeyPath: "updatedAt", ascending: false )
// observe changes
plansChangedObservationToken = chats.addNotificationBlock { [weak self] (changes:RealmCollectionChange) in
guard let chatsList:UITableView = self?.chatsList else { return }
switch changes {
case .initial:
chatsList.reloadData()
case let .update( chat, deletions, insertions, modifications ):
chatsList.beginUpdates()
chatsList.insertRows( at: insertions.map({ IndexPath( row: $0, section: 0 )}), with: .automatic )
chatsList.deleteRows( at: deletions.map({ IndexPath( row: $0, section: 0 )}), with: .automatic )
chatsList.reloadRows( at: modifications.map({ IndexPath( row: $0, section: 0 )}), with: .automatic )
try chatsList.endUpdates()
case .error( let error ):
assertionFailure( "Could not initialize chats changed Realm observation. Error: \(error)" )
}
}
}
When our background sync does a full sync and processes all the sync operations (adding/updating/removing them from the local Realm DB) we get the following error:
Getting error:
2017-05-14 17:06:07.015 Happ.Mobile[24874:1424540] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 13 from section 0 which only contains 10 rows before the update'
It seems the table-view gets out of sync with what is notified in the sync token.
Question 1) Is there any way to tell a realm to only fire notifications at specific times (ie: at the end of our sync loop, so that updates only get pushed up once?
Question 2) Is there some way to synchronize the table-view and the NotificationToken so that it only applies the deltas to the table-view once they've been fully applied? (it seems right now it's firing faster than the table-view is able to apply them)
We tried applying insertRows/updateRows etc. without animations (".none" for the last param) but it still fails in the same way.
Any update on this? Using Realm change notifications seems wildly broken/unusable until this issue is fixed. Having the limitation that change notifications need to happen "slower" than the UI can update seems completely infeasible – against the whole point of performing operations on BG threads and the UI staying in sync on its own.
Please create a new issue. Commenting on a long-closed issue is a good way to ensure that things get missed.
Most helpful comment
Any update on this? Using Realm change notifications seems wildly broken/unusable until this issue is fixed. Having the limitation that change notifications need to happen "slower" than the UI can update seems completely infeasible – against the whole point of performing operations on BG threads and the UI staying in sync on its own.