Dawy
Dawy

Reputation: 946

How to deal with master/detail CoreData between SwiftUI views

I'm dealing with issue how to pass parameter selected in master view to CoreData predicate in detail view. I have this master view

struct ContentView: View {

@State private var selectedCountry: Country?
@State private var showSetting = false

@FetchRequest(entity: Country.entity(),
              sortDescriptors: [NSSortDescriptor(keyPath: \Country.cntryName, ascending: true)]
) var countries: FetchedResults<Country>

var body: some View {
    NavigationView {
        VStack {
            Form {
                Picker("Pick a country", selection: $selectedCountry) {
                    ForEach(countries, id: \.self) { country in
                        Text(country.cntryName ?? "Error").tag(country as Country?)
                    }
                }
                if selectedCountry != nil {
                    Years(cntryName: selectedCountry?.cntryName! ?? "")
                }
            }
        }
        .navigationBarTitle("UNECE Data")
        .navigationBarItems(trailing: Button("Settings", action: {
            self.showSetting.toggle()
        }))
    }
    .sheet(isPresented: $showSetting) {
        SettingsView(showSetting: self.$showSetting)
    }
}
}

where I use Picker to select country name (from CoreData entity Country and its attribute cntryName) and pass it as String value to Years view which is coded like this

struct Years: View {

var cntryName: String
@State private var selectedDataRow: Data?
@State private var result: NSFetchRequestResult

@FetchRequest(entity: Data.entity(),
              sortDescriptors: [NSSortDescriptor(keyPath: \Data.dataYear, ascending: true)],
              predicate: NSPredicate(format: "dataCountry == %@", "UK"), animation: .default
) var data: FetchedResults<Data>

var body: some View {
    Picker("Year", selection: $selectedDataRow) {
        ForEach(data, id: \.self) { dataRow in
            Text(dataRow.dataYear ?? "N/A")
        }
    }
    .pickerStyle(WheelPickerStyle())
    .frame(width: CGFloat(UIScreen.main.bounds.width), height: CGFloat(100))
    .clipped()
    .onAppear() {
        let request = NSFetchRequest<Data>(entityName: "Data")
        request.sortDescriptors = [NSSortDescriptor(key: "dataYear", ascending: true)]
        request.predicate = NSPredicate(format: "dataCountry == %@", self.cntryName)
        do {
            self.result = try context.fetch(request) as! NSFetchRequestResult
            print(self.result)
        } catch let error {
            print(error)
        }
    }
}
}

It works fine with @FetchRequest and FetchedResults stored in var data but I'm wondering how to build predicate here based on passed country name. To overcome this I considered to use onAppear section and classic NSFetchRequest and NSFetchRequestResult which causes compiler error "'Years.Type' is not convertible to '(String, NSFetchRequestResult, FetchRequest) -> Years'" in the line

Years(cntryName: selectedCountry?.cntryName! ?? "")

of ContentView struct. Error disappear if I comment the line

@State private var result: NSFetchRequestResult

in Years struct but it obviously causes another error. So I'm lost in circle. What`s recommended practice here, please? Thanks.

Upvotes: 2

Views: 334

Answers (1)

Dawy
Dawy

Reputation: 946

Finally I found the way thanks to this post SwiftUI use relationship predicate with struct parameter in FetchRequest

struct Years: View {

var request: FetchRequest<Data>
var result: FetchedResults<Data> {
    request.wrappedValue
}

@State private var selectedDataRow: Data?

init(cntryName: String) {
    self.request = FetchRequest(entity: Data.entity(),
                           sortDescriptors: [NSSortDescriptor(keyPath: \Data.dataYear, ascending: true)],
                           predicate: NSPredicate(format: "dataCountry == %@", cntryName), animation: .default)
}

var body: some View {
    VStack {
        Picker("Year", selection: $selectedDataRow) {
            ForEach(result, id: \.self) { dataRow in
                Text(dataRow.dataYear ?? "N/A").tag(dataRow as Data?)
            }
        }
        .pickerStyle(WheelPickerStyle())
        .frame(width: CGFloat(UIScreen.main.bounds.width), height: CGFloat(100))
        .clipped()
        VStack(alignment: .leading, spacing: 10) {
        HStack {
            Text("Total polutation: ")
                .alignmentGuide(.leading) { dimension in
                    10
            }
            if selectedDataRow != nil {
                Text(String(describing: selectedDataRow!.dataTotalPopulation))
            } else {
                Text("N/A")
            }
        }
        }}
}
}

Upvotes: 0

Related Questions