Search code examples
swiftrealmtableview

Realm - Update List Order


I've trying to reorder objects from at TableView in a Realm utility class. Many other Stack Overflow questions have said to uses List, however I can't seem to make it work. (Example, example). I'm able to successfully add objects to my list with:

public func addUserSong(song: Song) {
    let songList = List<Song>()
    songList.append(objectsIn: realm.objects(Song.self))
    try! realm.write {
        songList.append(song)
        realm.add(songList)
    }
}

However, I'm not able to preserve the updated order when trying:

public func reorder(from: Int, to: Int) {
    let songs = List<Song>()
    songs.append(objectsIn: realm.objects(Song.self))
    
    songs.move(from: from, to: to)
    
    try! realm.write {
        realm.add(songs)
 }

My models are:

 class Song: Object {
   @Persisted var name: String
   @Persisted var key: String
 }

class SongList: Object {
   let songs = List<Song>()
}

Thanks!


Solution

  • Realm object order is not guaranteed. (unless you specify a sort order)

    e.g. if you load 10 songs from Realm, they could come into your app an any order and the order could change between loads. The caveat to that is a Realm List object. Lists always maintain their order.

    The problem in the question is you have Song objects stored in Realm but as mentioned above there is no ordering.

    So the approach needs to be modified by leveraging a List object for each user to keep track of their songs:

    class UserClass: Object {
       @Persisted var name: String
       @Persisted var songList = List<SongClass>()
    }
    

    When adding a song to a user, call it within a write transaction

    try! realm.write {
       someUser.songList.append(someSong)
    }
    

    suppose the user wants to switch the place of song 2 and song 3. Again, within a write transaction:

    try! realm.write {
       someUser.songList.move(from: 2, to: 3)
    }
    

    So then the UI bit - tableViews are backed by a tableView dataSource - this case it would be the songList property. When that dataSource is updated, the tableView should reflect that change.

    In this case you would add an observer to the someUser.songList and as the underlying data changes, the observer will react to that change and can then update the UI.

    You can do something simple like tableView.reloadData() to reflect the change or if you want fine-grained changes (like row animations for example) you can do that as well. In that same guide, see the code where tableView.deleteRows, .insertRows and .reload is handled. You know what rows were changed in the underlying data there so you can then animate the rows in the tableView.