SwiftUI / CoreData / Master / Detail (with editing

2020-02-08 21:40发布

问题:

Working on a sample app. The goal is to have a list pulled up from CoreData in a Master, and then click on one to go to a detail, where you can edit the information and save. When you edit the "name" in the detail, it not only updates the detail to reflect the change, but it also reflects the change on the master as well. I've tried numerous ways to accomplish this, but so far have not come up with an answer.

// Code generation is turned OFF in the xcdatamodeld file

public class EntityName: NSManagedObject, Identifiable {
   @NSManaged public var name: String
   @NSManaged public var active: Bool
}

extension EntityName {
    static func allEntityNameFetchRequest() -> NSFetchRequest<EntityName> {
        let request: NSFetchRequest<EntityName> = EntityName.fetchRequest() as! NSFetchRequest<EntityName>
        request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        return request
   }
}


struct MasterView: View {

    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(fetchRequest: EntityName.allEntityNameFetchRequest()) var allEntityNames: FetchedResults<EntityName>


    var body: some View {
        NavigationView {
            List {
                ForEach(self.allEntityNames) { entityName in
                NavigationLink(destination: DetailView(entityName: entityName)) {
                    VStack(alignment: .leading) {
                        Text(entityName.name)
                            .font(.headline)
                        Text(String(entityName.active))
                            .font(.subheadline)
                    }
                }
            }
        }
    }
    .onAppear() {
        // Just want to populate the Core Data to have a few to work with
        if self.allEntityNames.count == 0 {
            for _ in 1...3 {
                let newEntry = EntityName(context: self.managedObjectContext)
                newEntry.name = "New Entry"

                try! self.managedObjectContext.save()
            }
         }
      }
   }
}

struct DetailView: View {

   var entityName = EntityName()

   var body: some View {
       VStack {
           Text("Name: \(entityName.name)")
           Text("Active: \(String(entityName.active))")

           // What I'd like to do now:
              //TextField("", text: $entityName.name)
              //Toggle(isOn: $entityName.active)
       }
   }
}

回答1:

As promised, here is a link to sample SwiftUI program that uses CoreData. The current version compiles and runs on the GM release. It does not use the new @FetchRequest property wrapper because it does not suit all of my needs. I wrote a CoreDataDataSource class that does basically the same thing plus a lot more.

https://github.com/Whiffer/SwiftUI-Core-Data-Test



回答2:

You need to bind the entity in DetailView to the entity passed in from MasterView. Declare it like @Binding var entityName: EntityName



回答3:

As @trapper explained, the entityName has to be bound the the MasterView.

Updates are reflected when you add @ObservedObject in the DetailView as follows:

@ObservedObject var entityName:EntityName