Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fast notification updates lead to UITableView crashes #4425

Closed
asjesset opened this issue Dec 8, 2016 · 16 comments
Closed

fast notification updates lead to UITableView crashes #4425

asjesset opened this issue Dec 8, 2016 · 16 comments
Labels

Comments

@asjesset
Copy link

asjesset commented Dec 8, 2016

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!

@jpsim jpsim added the T-Help label Dec 8, 2016
@jpsim
Copy link
Contributor

jpsim commented Dec 8, 2016

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!

@asjesset
Copy link
Author

asjesset commented Dec 9, 2016

In our case the updates are in a background thread, not the UI thread. We will try updating and report back!

@danipralea
Copy link

@jpsim how to update to the 2.1.1 version?
Using pod 'RealmSwift' uses the 2.1.0 version

@jpsim
Copy link
Contributor

jpsim commented Dec 13, 2016

@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!

@danielrhodes
Copy link

@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.

@jpsim
Copy link
Contributor

jpsim commented Dec 16, 2016

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.

@danielrhodes
Copy link

@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.

@kishikawakatsumi
Copy link
Contributor

kishikawakatsumi commented Dec 17, 2016

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

@TimOliver
Copy link
Contributor

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!

@asjesset
Copy link
Author

asjesset commented Jan 6, 2017

I am not seeing this issue anymore. It's pretty challenging to determine though! Will post back if I see it again.

@Raghvendra7
Copy link

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) -> NotificationToken {
return tasks.addNotificationBlock {[weak self] (changes: RealmCollectionChange<Results>) in
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

@pigeondotdev
Copy link

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!

@marchy
Copy link

marchy commented May 14, 2017

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.

@marchy
Copy link

marchy commented Jun 14, 2017

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.

@tgoyne
Copy link
Member

tgoyne commented Jun 14, 2017

Please create a new issue. Commenting on a long-closed issue is a good way to ensure that things get missed.

@BrandonZacharie
Copy link

#4818 may be related which is open.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests