Learn Courses My Dashboard

Updating Data from picker

Hey guys,

Just hading some trouble updating my app data from my main screen picker. I have included the data structure and also the period and picker swift for review but the error which I am getting is on the MainView where I have:

PeriodPicker(selection: $data.mainFrequency)
                        .onChange( of: data.mainFrequency, perform: main.update(from: newPeriodData))

which is giving me an error: Cannot convert value of type ‘()’ to expected type ‘(Period) → Void’.

Any help here would be awesome. Thanks

import SwiftUI

struct MainView: View {
    @Binding var data: MainDetails.Data
    @Binding var main: MainDetails
    @Binding var income: [IncomeDetails]
    @State private var newPeriodData = MainDetails.Data()
    
    var period = ["Annually", "Quarterly", "Monthly", "Fortnightly", "Weekly", "Daily"]
        @State var selectedPeriod = "Annually"

    
    var body: some View {
        ZStack {
            VStack{
                HStack {
                    Image("abacus")
                        .aspectRatio(contentMode: .fit)
                        .padding(.bottom)
                Text("Budget Planner")
                    .font(.title)
                }
                
                Spacer()
                List {
                    PeriodPicker(selection: $data.mainFrequency)
                        .onChange( of: data.mainFrequency, perform: main.update(from: newPeriodData))
                    
                    NavigationLink(destination: IncomeView(income: $income)) {
                        MainIncomeView(data: $data, income: income) }
                    
                       NavigationLink(destination: IncomeView(income: $income)) {
                           MainIncomeView(data: $data, income: income) }
                }
                MainResultView()
                Spacer()
            }
            .environment(\.colorScheme, .dark)
        }
        .background(.black)
        }
}

struct MainView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            MainView(data: .constant(MainDetails.sampledata[0].data),main: .constant(MainDetails.sampledata[0]), income: .constant(IncomeDetails.sampledata))
    }
    }
}

import SwiftUI

enum Period: String, CaseIterable, Identifiable, Codable {
    case Daily
    case Weekly
    case Fortnightly
    case Monthly
    case Quarterly
    case Annually
    
    
    var name: String {
        rawValue.capitalized
    }
    var id: String {
        name
    }
}

import SwiftUI

struct PeriodPicker: View {
    @Binding var selection: Period
    
    var body: some View {
        Picker("Frequency", selection: $selection) {
            ForEach(Period.allCases) {
                period in
                    PeriodView(period: period)
                        .tag(period)
                        .environment(\.colorScheme, .dark)
            }
            .environment(\.colorScheme, .dark)
    }
        .environment(\.colorScheme, .dark)
}
}

struct PeriodPicker_Previews: PreviewProvider {
    static var previews: some View {
        PeriodPicker(selection: .constant(.Weekly))
            .environment(\.colorScheme, .dark)
    }
}

import SwiftUI

struct PeriodView: View {
    let period: Period
    
    var body: some View {
        ZStack {
            Label(period.name, systemImage: "calendar")
                .padding(4)
        .fixedSize(horizontal: false, vertical: true)
        .environment(\.colorScheme, .dark)
}
        .environment(\.colorScheme, .dark)
}
}

struct ThemeView_Previews: PreviewProvider {
    static var previews: some View {
        PeriodView(period: .Weekly)
    }
}

What does your MainDetails object look like? onChange is expecting a perform closure that looks like (Period) -> Void but you aren’t giving it that.

Judging by the rest of your code, it looks like you are providing a closure with a signature of (MainDetails.Data) -> Void

SwiftUI is expecting something like this:

.onChange(of: data.mainFrequency) { period in
    //period is of type Period
    //you can do something with this new Period here
}

Or something like this:

extension MainDetails {
    func updatePeriod(_ period: Period) {
        //update the period however you want
    }
}

//then in your View...
.onChange(of: data.mainFrequency, perform: main.updatePeriod)

Sorry I forgot to add this in , here is the MainDetails:

import Foundation

struct MainDetails: Identifiable {
    let id: UUID
    var mainFrequency: Period
    
    init(id: UUID = UUID(), mainFrequency: Period) {
        self.id = id
        self.mainFrequency = mainFrequency
    }
}

extension MainDetails {

struct Data {
    var mainFrequency: Period = .Annually
    }

var data: Data {
    Data(mainFrequency: mainFrequency)
}
    
    mutating func update(from data: Data) {
        mainFrequency = data.mainFrequency
    }
    
    init(data: Data) {
        id = UUID()
        mainFrequency = data.mainFrequency
    }
}

extension MainDetails {
    static let sampledata: [MainDetails] =
    [
        MainDetails(mainFrequency: .Annually)
    ]
}

So this has removed my error, but funny enough when I go to use my picker it doesn’t allow me to select my option. Any idea why that would be?

I have used this same code in another area of my app and the picker works fine.

PeriodPicker(selection: $data.mainFrequency)
                        .onChange( of: data.mainFrequency) { period in
                            main.update(from: newPeriodData)
                        }

main.update(from: newPeriodData)

So where is newPeriodData coming from? You initialize it here:

@State private var newPeriodData = MainDetails.Data()

but as far as I can see it never gets changed to anything so your main.update method will always update with the same value newPeriodData was initialized with.