Rayan Slim Rayan Slim - 17 days ago 4
Swift Question

Does realm have the equivalent of fetchResultsController's .NSFetchedResultsChangeMove:

The code I currently have for updating a tableView from realm collection changes is the following:

func updateUI(changes: RealmCollectionChange<Results<Task>>) {
switch changes {
case .Initial(_):
tableView.reloadData()
case .Update(_, let deletions, let insertions, let modifications):

tableView.beginUpdates()


if !(insertions.isEmpty) {

tableView.insertRowsAtIndexPaths(insertions.map {NSIndexPath(forRow: $0, inSection: 0)},
withRowAnimation: .Automatic)


}


if !(deletions.isEmpty) {

tableView.deleteRowsAtIndexPaths(deletions.map {NSIndexPath(forRow: $0, inSection: 0)},
withRowAnimation: .Automatic)



}

if !(modifications.isEmpty) {

tableView.reloadRowsAtIndexPaths(modifications.map {NSIndexPath(forRow: $0, inSection: 0)}, withRowAnimation: .Automatic)


}







tableView.endUpdates()
break



case .Error(let error):
print(error)
}
}


Having used Core-data instead of Realm before, fetchedResultsController had the very convenient method
NSFetchedResultsChangeMove
for when I sorted what was in core data. As shown in the apple documentation, when something moved, it's current position in the table is deleted and then inserted into a new position (Yes I do realize it's objective C and my code is swift, but it's a clear example).

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {

UITableView *tableView = self.tableView;

switch(type) {

case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;

case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}


}

As you can see from the code, realm seems to have all but the move parameter. As I'am making a chat app, move was invaluable when I was using core data and I'm hoping to replicate the same behaviour in Realm.
Thank you.

Answer

Including move operations in collection notifications is something we'd like to do. We're tracking that functionality here: https://github.com/realm/realm-cocoa/issues/3571

From that GitHub issue:

The good news is, Realm internally already computes move operations.

The bad news is, move operations are converted to insertions and deletions at the very end of the change calculation algorithm: https://github.com/realm/realm-object-store/blob/28ac73d8881189ac0b6782a6a36f4893f326397f/src/impl/collection_change_builder.cpp#L35-L38

I vaguely recall this being done due to UITableView APIs not handling move operations very nicely, that they would crash in certain scenarios despite having perfectly valid results. As this never happens with insert/delete pairs, and that this feature would be used to power UITableView's 99% of the time, we opted to "flatten" moves to work around this issue.