How to update an @ObservedRealmObject in a SwiftUI view

In the SwiftUI View code below, I am able to successfully add a Car object to the cars list of my AutoMaker class using the append function. That works fine. But when I try to increment another property on the AutoMaker class, I get an error “attempting to modify object outside of a write transaction .” I understand the error and I know that modifications need to be made within a write transaction, but I’m not sure what that code would look like.

I think there are two ways to handle this. One would be to add the write transaction in my addItem function in the view. And the other would be to add a method on my AutoMaker class, like incrementCount(), to do the incrementing, and then just call that function when I need to update the field. The write transaction would then be in that function on the AutoMaker class.

So I’m hoping someone can show me what the code would look like in both circumstances. What would that write transaction look like if I put it here in the addItem function of the view, and what might it look like as a function on the AutoMaker class itself?

Here is the view code:

struct AutoView: View {
    @ObservedRealmObject var autoMaker: AutoMaker
    @State private var text = ""
    
    var body: some View {
        VStack {
            HStack {
                TextField("Car Name", text: $text)
                Button(action: addItem) {
                    Text("Add")
                }
            }
            List {
                ForEach(autoMaker.cars) {car in
                    Text(car.name)
                }
                .onDelete(perform: $autoMaker.cars.remove)
            }
        }
    }
    
    func addItem() {
        autoMaker.numberOfAdditions += 1  // Exception caused by this line
        $autoMaker.cars.append(Car(name: text)) // This line works fine when the line above is commented out
        text = ""
    }
}

And here is the AutoMaker class object:

class AutoMaker: Object, ObjectKeyIdentifiable {
    @objc dynamic var name = ""
    @objc dynamic var numberOfAdditions: Int = 0
    var cars = RealmSwift.List<Car>()
    
    convenience init (name: String) {
        self.init()
        self.name = name
    }
}

(Note, this numberOfAdditions property is not the count of the number of cars. I know I can get that from a computed property. It has another purpose.)

Thanks.
–Tom

1 Like

Hi @TomF,

I’ll give you my temporary solution to try, but there should be a cleaner solution available from Realm-Cocoa sometime (hopefully soon):

do {
    try Realm().write() {
        guard let thawedAutoMaker = autoMaker.thaw() else {
            print("Unable to thaw autoMaker")
            return
        }
        thawedAutoMaker.numberOfAdditions += 1
    }
} catch {
    print("Failed to save autoMaker: \(error.localizedDescription)")
}
1 Like

Can you try:
$autoMaker.numberOfAdditions.wrappedValue += 1
?

1 Like

Both solutions worked. Thank Andrew and Jason. It makes sense to put the wrappedValue version in the view code to keep the view simpler:

func addItem() {
        $autoMaker.numberOfAdditions.wrappedValue += 1
        $autoMaker.cars.append(Car(name: text))
        text = ""
 }

Or if I want to add the incrementing code as a method on the AutoMaker class, I would use Andrew’s code:

func incrementNumber() {
        do {
            try Realm().write() {
                guard let thawedAutoMaker = self.thaw() else {
                    print("Unable to thaw autoMaker")
                    return
                }
                thawedAutoMaker.numberOfAdditions += 1
            }
        } catch {
            print("Failed to save autoMaker: \(error.localizedDescription)")
        }
 }  

And then call that in my view:

func addItem() {        
        autoMaker.incrementNumber()
        $autoMaker.cars.append(Car(name: text))
        text = ""
 }

–Tom

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.