Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Navigating to detail views in SwiftUI using core data

I'm trying to build an example BBQ app to learn SwiftUI (XCode 11 beta 5), and have been unable to figure out how to navigate to an object's detail view from a list view of objects coming from Core Data. Xcode is unhelpful, mostly popping unrelated errors depending on what I try.

I've tried applying state, bindings, observable objects etc to the best of my logical ability but haven't been able to crack it.

Here is my list view, which builds just fine, and allows me to navigate to destination (which is just a view containing the id property as a string):

struct CookListView: View {

    @ObservedObject var cookListVM: CookListViewModel
    @State var cookCreatorVM = CookCreatorViewModel()

    init() {
        cookListVM = CookListViewModel()
    }

    var body: some View {


        NavigationView {
            List {
                ForEach(cookListVM.cooks, id: \.id) { cook in
                    NavigationLink(destination: Text("\(cook.id)")) {
                        VStack {
                            Text(cook.protein)
                        }
                    }
                }
            }.navigationBarTitle("BBQ")
        }
    }
}

But if I change my NavigationLink destination like so:

NavigationView {
    List {
        ForEach(cookListVM.cooks, id: \.id) { cook in
            NavigationLink(destination: CookDetailView(cook: cook)) {
                VStack {
                    Text(cook.protein)
                }
            }
        }
    }
}

Xcode will no longer build the project. The errors it gives me seem somewhat unrelated, such as Type '_' has no member 'id' on the ForEach line, or if I remove , id: \.id from the ForEach (which I don't really need thanks to Identifiable), I'll get Type of expression is ambiguous without more context on the Text(cook.protein) line.

If I use a hard-coded array it builds and can navigate perfectly well. The issue only arises when I'm trying to use Core Data.

My CookDetailView looks like this:

struct CookDetailView: View {
    var cook: Cook
    var body: some View {
        VStack {
            Text("\(cook.protein!)")
        }
    }
}

And the model for the Cook object itself looks like this:

class CookViewModel: ObservableObject, Identifiable {

    var protein: String = ""
    var type: String = ""
    var id: UUID = UUID()

    init(cook: Cook){
        self.protein = cook.protein!
        self.type = cook.type!
        self.id = UUID()
    }
}

It's also been set up thru the .xcdatamodeld file.

I'm more than happy to add in any additional/omitted code, such as how I'm writing to/reading from core data, if that would be helpful.

like image 598
immediateorchestra Avatar asked Jan 21 '26 02:01

immediateorchestra


1 Answers

I can totally identify with your frustration with Xcode's error messages. The actual error is often nowhere near the error message. [Sug: use source control and commit after each clean compile ;-) ] The error that you are getting in your List view is because you specified a wrong type in your detail view's argument list. Try the following:

struct CookDetailView: View {

    var cook: CookViewModel

    var body: some View {
        VStack {
            Text("\(cook.name)")
        }
    }
}

By the way, since you are fetching into an array, you will not observe changes in that array unless you manually publish them. I gave up on trying to get my NSManagedObject subclasses to publish their own changes. There are two ways to workaround that. You can call objectWillChange from NSFetchedResultsController's controllerDidChangeContent delegate or you can do the same while observing NSManagedObjectContextDidSave notifications when not using a fetched results controller.

This answer is applicable to Beta 5. Things will certainly change in future betas.

like image 62
Chuck H Avatar answered Jan 23 '26 20:01

Chuck H