Deleting from RealmSwift.List<Object> raises out of bounds exception

I have been trying to follow the ListSwiftUI example provided in the realm-cocoa repo. It has been working very well for me but I have run into a problem, when I try to delete from a RealmSwift.List<Object> the app crashes with an exception "Terminating app due to uncaught exception 'RLMException', reason: 'Index 5 is out of bounds (must be less than 5).'"

Model:

final class Todo: Object, ObjectKeyIdentifiable {
    @objc dynamic var id: String = UUID().uuidString
    @objc dynamic var name: String = ""
    @objc dynamic var createdAt: Date = Date()
    
    override class func primaryKey() -> String? {
        "id"
    }
}

final class Todos: Object {
    @objc dynamic var id: Int = 0
    
    let todos = List<Todo>()
    
    override class func primaryKey() -> String? {
        "id"
    }
}

SceneDelegate.swift

let realm = try! Realm()
var todos = realm.object(ofType: Todos.self, forPrimaryKey: 0)
if todos == nil {
    todos = try! realm.write { realm.create(Todos.self, value: []) }
}

// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(todos: todos!.todos)

ContentView.swift

@ObservedObject var todos: RealmSwift.List<Todo>
var body: some View {
    ForEach(todos) { (todo: Todo) in
        Text("\(todo.name)")
        .onDelete(perform: delete)
}

func delete(at offsets: IndexSet) {
    if let realm = todos.realm {
        try! realm.write {
            realm.delete(todos[offsets.first!])
        }
    } else {
        todos.remove(at: offsets.first!)
    }
}

I have checked the count of my todos list and the db in Realm Studio, it actually contains the index 5. And this crash is not specific to only index 5, e.g. if I only have 2 items in the list, the app crashes with Index 1 or when it contains 5 items, it crashes with Index 4.

The example has pretty much the same Data Models and it works perfectly. So what am I doing wrong here?

Hello, I encountered the same error as you, the data I observed by printing the array length and index is normal. . .

@G_X

There were a few issues here - one of which is there wasn’t enough data presented - what is offsets and why is it force unwrapped (never a good idea). The second issue is that an Array and List are different things; if an object that is part of of a List is deleted from Realm, the List knows that and it’s removed - preventing you from trying to access an object that no longer exists. Array’s on the other hand do not do that and if your array contains 5 Realm objects that have all been deleted, well… it will crash when trying to access them.

This is a two-year old question and if you have a question, I suggesting posting it and your code as a duplicatable example so we can take a look.

@Jay
Thanks a lot for your answer, yesterday I solved the problem in a very inelegant way, today I’m thinking about refactoring it.
My data structure is that there is a List property in a Realm object, which stores some embedded objects. I traverse these embedded objects in the view and perform some delete, add, and modify operations on them. Since the object is an embedded object, I can’t use @ObservedResults, but at the same time I want the state of the UI to be automatically bound to the data, so I get the object from the List property of the Realm object that can perform @ObservedResults through index and Binding to the UI, at this time the problem arises, there is no problem with modification and addition, but when it is deleted, an error will occur. Finally, my solution is to enter the view to judge whether the index exceeds the length of the list, and if so, set index-1 for normal rendering.

@G_X

I may not be fully understanding, but if the embeddedObjects are stored in a List, which in itself is not an embedded object - it’s fully observable via keypath. Maybe a separate question with your code and use case would help.