Home > Net >  Passing NSManagedObject to child context not working
Passing NSManagedObject to child context not working

Time:11-06

I'm new to programming and if someone could provide a basic explanation that would be sincerely appreciated.

I am trying to pass an NSManagedObject that I have fetched and is associated with the main view context to a child context for editing. However, nothing is showing up in my detail view. For some reason the managed object isn't being passed.

// Content View, everything is showing up fine here

struct ContentView: View {
    let persistenceController = PersistenceController.shared
    @Environment(\.managedObjectContext) private var viewContext
    @StateObject var viewModel = ContentViewModel()
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Person.name, ascending: true)],
        animation: .default)
    private var people: FetchedResults<Person>
    
    var body: some View {
        NavigationView {
            List {
                ForEach(people) { person in
                    NavigationLink(destination: EditView(viewModel: EditViewModel(persistenceController: persistenceController, person: person))) {
                        Text(person.name ?? "Unknown")
                        Text("\(person.objectID)")
                    }
                }
            }

// EditViewModel 

struct EditViewModel {
    var person: Person
    let context: NSManagedObjectContext
    let persistenceController: PersistenceController
    
    init(persistenceController: PersistenceController, person: Person) {
        self.context = persistenceController.childViewContext()
        self.person = (persistenceController.childViewContext().object(with: person.objectID) as? Person)!
        self.persistenceController = persistenceController
    }

// EditView - all person properties are nil

struct EditView: View {
    @State var viewModel: EditViewModel
    var body: some View {
        GeometryReader { proxy in
            ScrollView(.vertical) {
                VStack {
                    TextField("Name", text: $viewModel.person.name ?? "")
                    Button("print") {
                        print(viewModel.person.name)
                    }
                }

Please let me know if I have failed to provide enough information to produce a minimum reproducible example and I will add more.

Thanks in advance.

CodePudding user response:

Since you are creating and injecting the view model into your edit view from your content view you don't need to use/pass your PersistenceController class and also since you are passing a Person object we don't need a NSManagedObjectContext either since the Person object has a reference to its context.

So I would rewrite the view model like this

struct EditViewModel {
    var person: Person
    let context: NSManagedObjectContext?
    let parentContext: NSManagedObjectContext

    init(person: Person) {
         guard let parentContext = person.managedObjectContext else {
             //no context is really bad and should never happen
             FatalError("Found a NSManagedObject that doesn't belong to a context")

         self.parentContext = parentContext 
         let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
         childContext.parent = parentContext

         guard let childPersopn = try? childContext.existingObject(with: person.objectID)) else { 
             self.childContext = nil        
             self.person = person
             return
         }
         self.context = childContext
         self.person = childPerson
    }
    //...
}

I find it a bit strange that the view model is a struct and not a class. You could make it into a class that conforms to ObservableObject and declare it as a @StateObject instead in your view.

  • Related