TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

NSManagedObject not reflecting changes when stored in a struct

Forums > SwiftUI

Hello everyone,

I'm building a SwiftUI app (iOS 15+) using Core Data. My post is simply out of interest, as I already found a workaround for the issue I had, but if someone could explain me why I had to do it this way, I'd be more than happy to learn why!

(For the sake of simplicity I show you a simplistict version of my code with only the interesting parts.)

After being able to add and list items (using Paul's awesome articles), I of course wanted to edit items! This is where I ended up watching Donny Wals' talk about this and followed his approach (https://youtu.be/P8rqjs_CNsk?t=1700) of using a viewModel which stores a temporary NSManagedObject in a child context, before persisting it to the main context when we are done. Clever.

In his video the viewModel is a struct, which is stored as a @State variable in the editing View. In my case I have some controls in this editing View which appear/disappear/change depending of other values the user changes (switchs, fields, whatever…).

My initial viewModel based on Donny Wals (persistenceController functions can be found in his talk, but I don't belive it really matters for my problem):

struct EditViewModel {
    var item: Item // This is a NSManagedObject

    private let childContext: NSManagedObjectContext
    private let persistenceController = PersistenceController.shared

    init(item: Item? = nil) {
        self.childContext = persistenceController.childViewContext()

        if let item = item {
            self.item = persistenceController.copyForEditing(of: item, in: childContext)
        } else {
            self.item = persistenceController.newTemporaryInstance(in: childContext)
        }
    }

    // More functions below to persist changes, etc.
}

And my EditView:

struct EditView: View {
    @State private var vm: EditViewModel

    init(item: Item? = nil) {
        _vm = State(initialValue: EditViewModel(item: item))
    }

    var body: some View {
        Form {
            Text(vm.item.text) // Should update when we change text in the TextField below.
            TextField("", text: $vm.item.text)
        }
    }
}

With this code, the issue I've faced is the Text view doesn't seem to reflect changes when we edit the TextField.

Changing my viewModel for a class does the trick, and it works fine:

class EditViewModel: ObservableObject {
    @Published var item: Item // This is a NSManagedObject

    // ...
}

The big question is why! Why do I have to use an ObservableObject. From what I understood, a struct as a @State should behaves the same, no? Is this related to the NSManagedObject under-the-hood architecture?

Thanks for enlightening me on this weird thing!

2      

Is this related to the NSManagedObject under-the-hood architecture?

Yep. Since an NSManagedObject is a reference type, changing a property on the object doesn't change the value of the object, which means your struct, which is a value type, doesn't change either, which means SwiftUI doesn't recognize that your state has changed and won't refresh the View.

3      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.