Reputation: 2644
Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
All changes to a managed object (addition, modification and deletion) must be done within a write transaction. For example,
// Update an object with a transaction
try! realm.write {
author.name = "Thomas Pynchon"
}
I can make a Realm sub-class conform to ObservableObject. However, I don't see how to make the realm properties updatable in SwiftUI. Realm property example below.
@objc dynamic var myName: String = "Adam"
Realm automagically sets up the schema based on @objc dynamic var. I don't see a way to get @Published on a realm property. SwiftUI will render a TextField, but crashes when the value is edited.
TextField("Quantity (\(shoppingItem.myUnit!.myName))", value: $shoppingItem.stdQty, formatter: basicFormat)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numbersAndPunctuation)
Is there any way to wrap SwiftUI state changes inside a Realm write transaction?
Upvotes: 1
Views: 1274
Reputation: 111
Another way to do this is to use a Custom Binding, and when setting the property, open a transaction and save data to realm.
TextField("Quantity (\(shoppingItem.myUnit!.myName))",
value: Binding(get: {shoppingItem.stdQty},
set: {(value) in
//if your state property is in a view model, all this code could be like viewModel.saveData(newMessage: value)
let realm = try! Realm()
try! realm.write {
shoppingItem.stdQty = value
}
}),
formatter: basicFormat)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numbersAndPunctuation)
This will save to realm on every character inserted
Upvotes: 1
Reputation: 2644
Consider the Realm property, stdQty shown below. It can only be changed within a write transaction.
import RealmSwift
import Combine
class ShoppingItems: Object, ObservableObject
let objectWillChange = PassthroughSubject<Void, Never>()
@objc dynamic var stdQty: Double = 1
You cannot bind stdQty without the error in the original question. However you can create a calculated variable that can be bound.
var formQty: Double {
get {
return stdQty
}
set {
objectWillChange.send()
if let sr = self.realm {
try! sr.write {
stdQty = newValue
}
}
}
}
Binding the calculated variable works fine.
TextField("Quantity (\(shoppingItem.myUnit!.myName))", value: $shoppingItem.formQty, formatter: basicFormat)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numbersAndPunctuation)
Answer limitation: objectWillChange is only triggered by the calculated variable. Changes in the form are reflected in the form. Changes in the realm don't trigger a Combine objectWillChange yet.
Upvotes: 0