Update Additional Field on Realm Object with ObservedRealmObject in SwiftUI

Viewed 601

Xcode 12.5, iOS 14.5, macOS 11.3

Let's say I have a simple Realm class like this in a SwiftUI app:

class Item: Object, ObjectKeyIdentifiable{
  @objc dynamic var id = ""
  @objc dynamic var name = ""
  @objc dynamic var updated = Date()
}

I pass an object into a view using @ObservedRealmObject where I can edit its name property by binding it to a TextField view like this:

struct DetailView: View {
  @ObservedRealmObject var item: Item

  var body: some View{
  
    TextField("Name...", $item.name)

  }
}

When I edit the field, the name property updates as I type and the realm is updated in real-time.

But I also want to update the updated property with a new Date() timestamp whenever the object is modified. Since the write transaction is managed automatically by @ObservedRealmObject, I'm unclear on how to pull this off.

How can I change updated every time the name (or any other additional property) is changed?

1 Answers

There are a lot of ways this can be accomplished; from simply updating the updated property when the 'save' button is pressed to using KVO to observe changes to the objects name property to adding a Swift 'front end' to the objects objc properties that can be handled through Computed Properties.

Let's use the third option using Swift Computed Properties and modify the Item in the question.

class Item: Object, ObjectKeyIdentifiable{
    @objc dynamic var _id = UUID().uuidString
    @objc private dynamic var _name = ""
    @objc dynamic var updated = Date()
    
    var name: String {
        get {
            return _name
        }
        set {
            _name = newValue
            updated = Date()
        }
    }
    
    convenience init(name: String) {
        self.init()
        self.name = name
    }
    
    override static func primaryKey() -> String? {
        return "_id"
    }
}

A few things to note:

The realm name property was made private with a name change to _name. I did that so you don't accidentally set it and go around the Swift implementation.

We then added a new Swift name property that acts as the 'front end' property to a Realm property backed by _name.

When an item is created using the convenience function let i = Item(name: "Item 0") or set within a write block someItem.name = "Updated name" the name computed property sets the Realm property and also populates the updated property

set {
   _name = newValue
   updated = Date()
}

This should work seamlessly with @ObservedRealmObject

Related