How to Make a Property Wrapper Observable in Swift?

Hello everyone,

I'm working with a User model in Swift, simplified as:

@Model
class User: Decodable {
    var name: String
}

To avoid duplicating the logic of fetching the first user from the database, I've implemented a property wrapper @UserWrapper. This wrapper fetches the user and returns it as a wrapped value. My goal is to observe any changes to the user in the database and update the views accordingly.

Here's my property wrapper:

@Observable
@propertyWrapper class UserWrapper {
        var user: User? = .none
    
    init() {
        guard let container = try? ModelContainer(for: User.self) else { return }
        let context = ModelContext(container)
        self.user = try? context.fetch(FetchDescriptor<User>()).first
        let didSaveNotification = Notification.Name.NSManagedObjectContextDidSave
        NotificationCenter.default.addObserver(self, selector: #selector(didSave(_:)),
                                                name: didSaveNotification, object: nil)
    }
    
    @objc func didSave(_ notification: Notification) {
        guard let container = try? ModelContainer(for: User.self) else { return }
        let context = ModelContext(container)

        let insertedObjectsKey = ModelContext.NotificationKey.insertedIdentifiers.rawValue
        let deletedObjectsKey = ModelContext.NotificationKey.deletedIdentifiers.rawValue
        let updatedObjectsKey = ModelContext.NotificationKey.updatedIdentifiers.rawValue
        
        if let insertedObj = notification.userInfo?[insertedObjectsKey] as? Set<NSManagedObject> {
            let objectIDs = insertedObj.map(\.objectID)
        }
        
        if let deletedObj = notification.userInfo?[deletedObjectsKey] as? Set<NSManagedObject>  {
            print("deletedObj \(deletedObj)")
        }
        
        if let updatedObj = notification.userInfo?[updatedObjectsKey] as? Set<NSManagedObject>  {
            print("updatedObj \(updatedObj)")
        }
        
    }
    
    var wrappedValue: User? {
        get {
            return user
        }
        set {
            self.user = newValue
        }
    }
}

Is there a more efficient or cleaner approach to achieve this? Any suggestions or best practices would be greatly appreciated!

is your user model shared across the app?

maybe try supplying it as a custom1 EnvironmentValue2 ?

When its updated, your views should automatically reflect those changes...

1 Like

Absolutely, you make a great point. I hadn't considered utilizing the environment. Thank you for bringing it to my attention.

1 Like