UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Loading entries from One-to-Many Relationship (Core Data)

Forums > SwiftUI

Hi All,

I thought to challenge myself in that I'd modify the candy project (Project 12 of 100 days of SwiftUI) to learn more about Core Data.

Here is what I managed so far:

I've modified the ContentView.swift file so that the countries appear as rows on my form with a navigationlink to custom DetailView.swift. The DetailView does list the respective candies. I can also add new countries using a toolbaritem -> opening a custom AddCountryView.swift file as a sheet. This works. Saving the new countries also works fine. They do appear in m main view afterwards.

Here is what doesn't work, though: Adding a new candy. Once inside DetailView.swift I can tap the toolbarItem to create a new candy using a custom AddCandyView. So here is the issue: Either saving the new candy doesn't work - or letting in appear in DetailView afterwards. I'm not exactly sure which of these two is the issue, I'm afraid.

However, I've been working on this for two days, but the nearast I could get to is an Error "Missing argument for parameter "country" in call." on this: ".sheet(isPresented: $showingAddCandyView, content: { AddCandyView()" in DetailView. Unfortunately, I'm not yet experienced enough to have a clue of what to insert here. Next to that I seem to be to stupid to insert a screenshot here...

Anyway, I would be greatful if anyone could push me in the right direction with this!

Many Thanks!

Pascal


import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Country.entity(), sortDescriptors: []) var countries: FetchedResults<Country>

    @State private var showingAddCountry = false

    var body: some View {
        NavigationView {
            Form {
                ForEach(countries, id: \.self) { country in
                    NavigationLink(destination: DetailView(country: country)) {
                        Text(country.wrappedFullName)
                    }
                }.onDelete(perform: deleteCountry)

                Button("Add") {
                    let candy1 = Candy(context: self.moc)
                    candy1.name = "Mars"
                    candy1.origin = Country(context: self.moc)
                    candy1.origin?.shortName = "UK"
                    candy1.origin?.fullName = "United Kingdom"

                    let candy2 = Candy(context: self.moc)
                    candy2.name = "KitKat"
                    candy2.origin = Country(context: self.moc)
                    candy2.origin?.shortName = "UK"
                    candy2.origin?.fullName = "United Kingdom"

                    let candy3 = Candy(context: self.moc)
                    candy3.name = "Twix"
                    candy3.origin = Country(context: self.moc)
                    candy3.origin?.shortName = "UK"
                    candy3.origin?.fullName = "United Kingdom"

                    let candy4 = Candy(context: self.moc)
                    candy4.name = "Toblerone"
                    candy4.origin = Country(context: self.moc)
                    candy4.origin?.shortName = "CH"
                    candy4.origin?.fullName = "Switzerland"

                    try? self.moc.save()
                }
            }
            .navigationTitle("CandyCountries")
            .navigationBarItems(leading: EditButton(), trailing: Button(action: {
                self.showingAddCountry.toggle()
            }) {
                Image(systemName: "plus")
            })
            .sheet(isPresented: $showingAddCountry, content: {
                AddCountryView()
            })
    //
        }
        }

    func deleteCountry(offsets: IndexSet) {
        for offset in offsets {
            let country = countries[offset]
            moc.delete(country)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
import SwiftUI

struct AddCountryView: View {
    @Environment(\.managedObjectContext) var moc
    @Environment(\.presentationMode) var presentationMode

    @State private var fullName = ""

    var body: some View {
        Form {
            TextField("New Country", text: $fullName)
            Section {
                Button("Save") {
                    let newCountry = Country(context: self.moc)
                    newCountry.fullName = self.fullName

                    try? self.moc.save()
                    self.presentationMode.wrappedValue.dismiss()
                }
            }
        }
    }
}

struct AddCountryView_Previews: PreviewProvider {
    static var previews: some View {
        AddCountryView()
    }
}
import SwiftUI

struct DetailView: View {
    @Environment(\.managedObjectContext) var moc
    @StateObject var country: Country

    @State private var showingAddCandyView = false

    var body: some View {
        Form {
            ForEach(country.candyArray) { candy in
                Text(candy.wrappedName)
            }
        }
        .navigationBarTitle("Candies")
        .navigationBarItems(trailing: Button(action: {
            self.showingAddCandyView.toggle()
        }) {
            Image(systemName: "plus")
        })

        //THIS IS WHERE THE ERROR APPEARS:

        .sheet(isPresented: $showingAddCandyView, content: {
            AddCandyView()
        })
    }
}

//struct DetailView_Previews: PreviewProvider {
//    static var previews: some View {
//        DetailView()
//    }
//}
import SwiftUI

struct AddCandyView: View {
    @Environment(\.managedObjectContext) var moc
    @Environment(\.presentationMode) var presentationMode
    @StateObject var country: Country

    @State private var name = ""

    var body: some View {
        Form {
            TextField("Custom Candy", text: $name)

            Section {
                Button("Save") {
                    let newCandy = Candy(context: self.moc)
                    newCandy.name = self.name

                    country.addToCandy(newCandy)
                    try? self.moc.save()
                    self.presentationMode.wrappedValue.dismiss()
                }
            }
        }
    }
}

//struct AddCandyView_Previews: PreviewProvider {
//    static var previews: some View {
//        AddCandyView()
//    }
//}

2      

hi,

just a few quick comments ...

  • in DetailView, you want @ObservedObject var country: Country, not a @StateObject (the DetailView does not own the country object; rather, the country is passed in to it).

  • in AddCandyView, the same comment applies, although i am not sure what value country has, since i don't see any assignment of a value to it. indeed, the country should be passed in from DetailView when the sheet is opened.

  • for clarity, i usually attach an explicit view modifier such as .environment(\.managedObjectContext, moc) to Views opened up in a sheet, since the sheet doesn't always get the environment of what opens it. so in DetailView, i'd recommend using

.sheet(isPresented: $showingAddCandyView, content: {
            AddCandyView()
            .environment(\.managedObjectContext, moc)
        })

hope some of this might be helpful,

DMG

3      

Hi DMG,

First of all many thanks for your quick reply!

Unfortunately, your suggestions didn't help with the error message. Still the same on that.

I should have added that I got the @StateObject idea from a YouTube tutorial, which teaches how to use one-to-many relationships in their app - though it's build slightly different (the new item gets created using a TextField above the list instead of in a seperated view.

https://www.youtube.com/watch?v=xgPlJXTfiNA

That, and that I'm still learning the ins and outs.

Anyway, again many thanks for your help!

Pascal

2      

UPDATE:

I changed the app to directly add candies ON DetailView() and that works just fine (But that's not a workaround I can life with). So it's clearly something in passing the data around when saving from AddCandyView()

Can anyone with more experience than I have spot the missing link?

Thanks,

Pascal

2      

Just to let you know: I was finally able to fix this issue after a good week. Here is the solution just in case anyone else stumbles over this in the future:

 .sheet(isPresented: $showingAddCandyView, content: {
            AddCandyView(country: country).environment(\.managedObjectContext, self.moc)
        })

If you asked me I'd swear to have tried "country: country" over and over again. Looks like I couldn't see the forest for the trees anymore.

2      

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.