Realm-cocoa: Recommended approach to sync table view with sections

Created on 4 Jan 2017  路  13Comments  路  Source: realm/realm-cocoa

Goals

Be able to sync in real time table view with sections and granular UI updates (add/remove/edit/move cells)

Expected Results

Can update table view correctly in all cases.

Actual Results

In some cases can't update table view correctly.

The main point of this issue is to find a recommended approach.

I got "almost everything" working, by putting all the items in a List, and doing the mapping of the linear list indices to sections at runtime. It's very finicky though. There are also some cases which doesn't seem solvable at all, like for example when moving a cell from the end of one section to the beginning of another. In this case the index of the cell in the underlaying List doesn't change - only its section. Sync sends me a modification in this case instead of the usual add/remove indices. How do I know what was the old section of the item? I can't, since the database was already updated. I could look for the item in the table view, but it's possible that the item is not visible, in which case there's no cell for it, so I just can't know from which section the item was moved. I maybe can compare the number of rows currently in the table view with the items in the database, to infer where an item was moved from/added to but this feels very cumbersome and error prone.

It's unwanted to just reload the complete table view on updates.

I thought about other approaches. For example, to store the items in the Realm with the same hierarchy used in the table view, i.e. having section realm objects that are in an ordered list and contain the list items. But this would mean I have to add a notification block to each section, where these sections of course are loaded dynamically and can be modified/deleted themselves. I doubt that notification blocks work well in such scenarios.

I also could cache all the items in memory in order to have the "old state" of the list available when I receive the notifications, in which case, for example I could get the old section of the item when the section is changed. But this means that I lose lazy loading and it also seems easy to end in an inconsistent state.

What is a recommended way to approach this? Is there maybe a way to access old versions of objects, or plans to send custom informations together with the updates or something else that could help?

T-Help

Most helpful comment

Ohh sorry, that doesn't look too good for my recommendation then :-(. I know the team has been exceptionally busy, so have likely prioritized the more burning bugs. But that obviously doesn't help you further. I'll ping the relevant people and see to that they get back to you. Thanks for your understanding!

All 13 comments

Hello,
is there something wrong with this question? I have noticed that after days it's the only one that doesn't even have a label yet. In that case please let me know to improve it, or revise my implementation, if I'm missing something obvious, thanks.

Hi @i-schuetz Sorry about that. You should have gotten some feedback, we are just very busy with an upcoming release, so slipped this one. That said, it's definitely better to ask "how-to" questions on StackOverflow as that's very well suited for those questions (i.e. multiple people will improve answers and they stay visible to others, where github is better for bugs as they are "closed" at some point and mostly gone...
If you ask on SO I'm sure people will jump on it, otherwise we will.

The reason I asked it here is because it was ignored in StackOverflow (was opened more than 2 weeks ago)....... :(

http://stackoverflow.com/questions/41305863/real-time-sync-with-table-view-sections

Ohh sorry, that doesn't look too good for my recommendation then :-(. I know the team has been exceptionally busy, so have likely prioritized the more burning bugs. But that obviously doesn't help you further. I'll ping the relevant people and see to that they get back to you. Thanks for your understanding!

@i-schuetz Would it be possible to store the objects in your model as they appear on screen , (as a [[Object]]) and then use realm to observe every sub array? (you would then have one notification block per section)

@hhanesand this would be one of the possible approaches I mentioned:

I thought about other approaches. For example, to store the items in the Realm with the same hierarchy used in the table view, i.e. having section realm objects that are in an ordered list and contain the list items. But this would mean I have to add a notification block to each section, where these sections of course are loaded dynamically and can be modified/deleted themselves.

I was not sure if the notification tokens work well in such dynamic scenario. It's basically the same as lists with items that can be modified/deleted themselves, but with the difference that you're using all the sections / listening to multiple notification blocks from the sections as well as the contained items at the same time. It may be that it works when I test it, but in some situations something weird happens. So I wanted to ask first. Is this recommended?

@i-schuetz sorry for a delay with the reply. Actually I don't think we have any recommended way to for your case, but to me personally it seems like having the same hierarchy in your database and listen for the notifications for each Section objects is a good idea. You should also have a notification handler for Sections query as well and unsubscribe from the specific Section notification in case it's deleted.

What issues did you have implementing this approach?

@stel no problem! I tried the index-based approach I described first and then decided to pause this until getting advice. I'll try it out soon. Seems the most coherent approach. I for some reason thought that frequently adding and removing notification blocks while also manipulating all the objects involved may lead to issues, but there doesn't seem to be a reason for these concerns. I'll update this thread when I implement it, may take some days until I get to do it.

Awesome @i-schuetz! Looking forward for the results :) Let us know if you have any issues or need any help.

Feel free to comment below or file a new issue if you do @i-schuetz! :)

This turned to be trivial, I was just missing that:

tableView.reloadSections(IndexSet(modifications), with: .none)

Animates modifications to the list items in the sections. Which makes it not necessary to observe the list items. So the approach of having Section realm objects that contain List of items, and listening only to the section's notifications works perfectly fine.

I just also realized that Realm's demo tasks app, where the different lists are implemented internally using List, the first having a dummy parent object, should also be able to use this instead of the granular item notifications (the notifications block would be attached to the respective parents, and then it's only needed to reload section 0)? The item notifications probably make sense for demo purposes though.

If only I knew this before trying out the other things. Wasted a lot of time ;)

Happy to hear that you've managed it to work properly, thanks for letting us know!

I'm use approach like in GroupedTableView example in RealmSwift, but with some modifications - I'm not reload tableView every time when Results change, instead I use array of NotificationToken to determine what sections and items was changed. It works perfectly by adding one new item manually. But I'm faced the problem with updating tableView during adding item by click to "BG add" button (insert few items in background).
The code is
objectsBySection.forEach { (section) in let notificationToken = section.observe({ (changes) in switch changes { case .initial(let items): self.tableView.reloadData() case .update(let items, deletions: let del, insertions: let ins, modifications: let modif): if let index = objectsBySection.index(of: items) { self.tableView.beginUpdates() self.tableView.insertRows(at: ins.map { IndexPath(item: $0, section: index) }, with: .left) self.tableView.deleteRows(at: del.map { IndexPath(item: $0, section: index) }, with: .fade) self.tableView.reloadRows(at: modif.map { IndexPath(item: $0, section: index) }, with: .none) self.tableView.endUpdates() } case .error(let error): print(error) } }) notificationTokens.append(notificationToken) }

Because of the model updates before observation block will called I get error

'Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).

How I can resolve it? Need some mechanism to determine when all items updated and after it update tableView.

Was this page helpful?
0 / 5 - 0 ratings