RealmSwift.List support for new editable List in SwiftUI

I have currently implemented this and it works. Here flowers is a List of Flower which is
a Realm.Object.

NavigationSplitView {
    List(selection: $selectedFlower) {
        ForEach($viewModel.flowers) { $flower in
            NavigationLink(value: flower) {
                FlowerRow(flower: flower)
            }
        }
        .onDelete { indexSet in
            withAnimation {
                selectedFlower = nil
                $viewModel.flowers.remove(atOffsets: indexSet)
            }
        }
    }
} detail: { /* Omitted */ }

SwiftUI (xCode14, beta 4) now provides an editable SwiftUI.List, making this more compact code possible:

NavigationSplitView {
    List($viewModel.flowers, editActions: [.delete], selection: $selectedFlower) { $flower in
        NavigationLink(value: flower) {
            FlowerRow(flower: flower)
        }
    }
} detail: { /* Omitted */ }

Attempting to do this with a RealmSwift.List gives the following error message:
‘delete’ is unavailable: Delete is available only for collections conforming to RangeReplaceableCollection.

RangeReplaceableCollection conformance requires an empty initializer and the
func replaceSubrange(Range<Self.Index>, with: C) method. These are both implemented by RealmSwift.List.

By declaring compliance to RangeReplaceableCollection in an extension to List I can get the code to
compile and run, unsurpisingly I get this error message.
‘Cannot modify managed RLMArray outside of a write transaction.’

If I then make a copy of replaceSubrange and for debugging purposes simplify it to the delete of a single Element
of the list and try to wrap that operation in a write transation like this:

extension List {

    public func replaceSubrange<C: Collection, R>(_ subrange: R, with newElements: C)
        where C.Iterator.Element == Element, R: RangeExpression, List<Element>.Index == R.Bound {
            let subrange = subrange.relative(to: self)
            
            let thawed = self.thaw() ?? self
            if let realm = thawed.realm, !realm.isInWriteTransaction {
                try! realm.write {
                    remove(at: subrange.lowerBound)
                }
            } else {
                remove(at: subrange.lowerBound)
            }
            
    }
}

I get this error message:
‘Object is already managed by another Realm. Use create instead to copy it into this Realm.’

If I check the realm objects of self and thawed it seems the thawing creates and new object with a different Realm.

(lldb) po thawed.realm
▿ Optional<Realm>
  ▿ some : Realm
    - rlmRealm : <RLMRealm: 0x60000018c6e0>

(lldb) po self.realm
▿ Optional<Realm>
  ▿ some : Realm
    - rlmRealm : <RLMRealm: 0x600000188370>

At this point I am stumped. Any suggestions?

Also in other places of my app I have found that if I directly use $list.append or remove
methods, things work, but as soon as I deviate from this I tend to end up with different list objects and multiple
realms. I would greatly appreciate an advanced tutorial on this topic!

I guess many developers would like to use the new cleaner approach to NavigationSplitView and List so it would
be good if you could get this to work.

As a side comment I have implemented my app on two different branches one for CoreData and one for Realm.
I very much prefer Realm with its bettter fit to Swift. However I found the topic of updating elements and
appending / removing from lists so complicated that at one point I gave up work on the Realm branch.

To summarize several days of investigation …

If I create a managed object in my App view (StateRealmObject) and pass it as an
argument to ContentView (ObservedRealmObject) the application fails with
“Object is already managed by another Realm”.

If I create the same StateRealmObject directly in ContentView the application actually works!

Very confusing to me. I do not see a reason for dual Realms to be created because a managed object
is passed in as an argument.

I have created a simplfied version of my app for this investigation. I include it here in full.
Any hint on how to proceed would be much appreciated.

SwiftListApp:

@main
struct SwiftListApp: SwiftUI.App {
    @StateRealmObject var viewModel: ViewModel = {
        try! Realm().write {
            let model = ViewModel()
            try! Realm().add(model)
            model.flowers.append(objectsIn: [
                Flower(index: 1, name: "one"),
                Flower(index: 2, name: "two")
            ])
            return model
        }
    }()
    
    var body: some Scene {
        WindowGroup {
            ContentView(viewModel: viewModel)
        }
    }
}

ContentView:

struct ContentView: View {
    @ObservedRealmObject var viewModel: ViewModel
    
    @State var selectedFlower: Flower?
    
    var body: some View {
        NavigationSplitView {
            List($viewModel.flowers, editActions: [.delete], selection: $selectedFlower) { $flower in
                NavigationLink(value: flower) {
                    Text("\(flower.index)")
                }
            }
            .navigationTitle("Blommor")
            .toolbar {
                EditButton()
            }

        } detail: {
            if let flower = selectedFlower {
                Text(flower.name)
            } else {
                Text("Select a flower")
            }
        }
    }
}

ViewModel:

class Flower: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var index: Int
    @Persisted var name: String
    
    /// Default initializer
    override init() {
        super.init()
    }
    
    convenience init(index: Int, name: String) {
        self.init()
        self.index = index
        self.name = name
    }
}

class ViewModel: Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var id: ObjectId
    @Persisted var flowers = RealmSwift.List<Flower>()
    
    /// Default initializer
    override init() {
        super.init()
    }
}

extension RealmSwift.List: RangeReplaceableCollection {
    
}

extension List {

    public func replaceSubrange<C: Collection, R>(_ subrange: R, with newElements: C)
        where C.Iterator.Element == Element, R: RangeExpression, List<Element>.Index == R.Bound {
            let subrange = subrange.relative(to: self)
            
            let thawed = self.thaw() ?? self
            if let realm = thawed.realm, !realm.isInWriteTransaction {
                try! realm.write {
                    thawed.remove(at: subrange.lowerBound)
                }
            } else {
                thawed.remove(at: subrange.lowerBound)
            }
            
    }
}

I am having a bit of trouble trying to replicate the issue. The gist of the problem is you’re instantiating a Realm Object, ViewModel, populating it with some data, returning that model and passing it to another view. That action throws the error. I wanted to try to address that part:

I’ve got a model TestModel

class TestModel: Object, ObjectKeyIdentifiable {
   @Persisted(primaryKey: true ) var id: ObjectId
   @Persisted var testField = ""
}

Here’s the @StateRealmObject that instantiates a model, stores it in Realm and then is used as an argument to the ContentView

struct Realm_SwiftUI_MacApp: SwiftUI.App {
    @StateRealmObject var testModel: TestModel = {
        try! Realm().write {
            let model = TestModel()
            try! Realm().add(model)
            model.testField = "Hello, World"
            return model
        }
    }()
    
    var body: some Scene {
        WindowGroup {
            ContentView(myTestModel: testModel)
        }
    }
}

and then the ContentView

struct ContentView: View {
    @ObservedRealmObject var myTestModel: TestModel
    .
    .
    .
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(testLabel) { labels in //this is just a list of a, b, c that are clickable
                        NavigationLink(destination: TestView(myObservedTestModel: myTestModel))
                        {
                            Text(testLabel.myId)
                        }

                    }
                }
            }

and then the TestView

struct TestView: View {
   @Binding var navigationViewIsActive: Bool
   @ObservedRealmObject var myObservedTestModel: TestModel

That all works correctly. The only little and probably not an issue thing is returning inside the write transaction?

Instead of this

try! Realm().write {
   return model
}

maybe this?

try! Realm().write {
   
}
return model

That’s probably not the issue.