TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Day 38 (Challenge for iExpense) - Missing arguments for parameters 'businessExpenses', 'personalExpenses' in call

Forums > SwiftUI

In my Expenses.swift file, I added two more classes, copies of Expenses from class and called the new classes BusinessExpenses and PersonalExpenses (using if type == "Business" and then if type == "Personal" before the HStack call (and duplicatiing the code was cauing an error and if I put it after the HStack, it was creating clank lines where the other type was reemoved. So, I decided to create two additional classes: one for BusinessExpenses and one for PersonalExpenses. I created separate delete actions for the two ForEach loops.

Everything (?) is working (well, compiling now)...except ContentView where the new sheet is presented to Add an Expense. The struct now has three @ObservedObject lines and therefore (?) needs all three passed in to AddView. When I re-type it, it now wants extra parameters...okay, that is good. I send in expenses, businessExpenses, and personalExpenses, three @StateObject variables I defined at the top of ContentView.

The problem, I think, is that it also wants body, it's showing, in the pop-up when I type "AddView(", two options: (expenses:businessExpenses:persoanlExpenses:name:type:amount:body) and (expenses:businessExpenses:personalExpenses:body:). I know why name, type, and amount are optional (they have defaults, like in class). Why now is body required? That means I did something wrong...any ideas? Anything I can check? I struggled with challenge 3 (to separate business and personal expenses) yesterday and slept on it and had this terrific idea to have three classes, and now I've hit this unfamiliar error. I searched Google, read Stack Overflow, and even Paul's discussion here on Hacking with Swift. Nothing talks about why body: is now on my required parameters list.

Or, any hints about how to complete step 3 of the challenge on day 38?

Thanks! Deb

1      

It would be helpful if you would show what your code currently is.

Enclose your code between two sets of 3 back ticks `

1      

Hi Deb,

I think you making it too complicated by the sounds of it.

Why do you not make two computed property of ExpenseItem, one personal and one for business then in your list have section of personal with a FoeEach loop over the personal array and one for Business and for each over the business array.

var personalItems: [ExpenseItem] {
    items.filter { $0.type == .personal }
}

2      

@NigelGee (Sorry, not sure how to reply to your reply...) part of the challenge is to be careful of deletion....I had added the If type == "Business" and the the HStack (and repeated that for If type == "Personal")...and that worked, untile there were twof them. If I ut them AFTER the HSTACK, it worked, If they were both befor ehte HSTACK, I got a compiler can't figure out what's happening. After the HSTACK, there were blank lines for the "missing" entries from expenses (of course, it wasn't finding anything to put in the view if the If statement wasn't true). If I don't figure out how to also delete from expenses, when I save as UserDefaults, won't the item not really be deleted? When I tried to use the same IndexSet, it deleted the wrong part from expenses...which is what the warning in the chalenge was about.

So, sorry if I'm being dense. The lightbulb hasn't gone off yet. I was making personalExpenses and businessExpenses classes (like Expenses) all with items which is [ExpenseItem] type in Expenses.swift and saving them with the original expenses struct in UserDefaults (with different key values. Then the ForEach was working in ContentView...but my AddView is complaining that I'm missing parameters.

I made some changes, trying to understand your suggested approach:

This is from the Expenses.swift file and holds the class definitions. I had three of them, then commented them out to go in a different direction:

//
//  Expenses.swift
//  iExpense
//
//  Created by Deborah Graham on 2/20/22.
//

import Foundation

//class BusinessExpenses: ObservableObject {
//    @Published var items = [ExpenseItem]() {
//        didSet {
//            if let encoded = try? JSONEncoder().encode(items) {
//                UserDefaults.standard.set(encoded, forKey: "BusinessItems")
//            }
//        }
//    }
//    
//    init() {
//        if let savedItems = UserDefaults.standard.data(forKey: "BusinessItems") {
//            if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from: savedItems) {
//                items = decodedItems
//                
//                return
//            }
//        }
//        items = []
//    }
//}
//class PersonalExpenses: ObservableObject {
//    @Published var items = [ExpenseItem]() {
//        didSet {
//            if let encoded = try? JSONEncoder().encode(items) {
//                UserDefaults.standard.set(encoded, forKey: "PersonalItems")
//            }
//        }
//    }
//    
//    init() {
//        if let savedItems = UserDefaults.standard.data(forKey: "PersonalItems") {
//            if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from: savedItems) {
//                items = decodedItems
//                
//                return
//            }
//        }
//        items = []
//    }
//}
class Expenses: ObservableObject {
    @Published var items = [ExpenseItem]() {
        didSet {
            if let encoded = try? JSONEncoder().encode(items) {
                UserDefaults.standard.set(encoded, forKey: "Items")
            }
        }
    }

    init() {
        if let savedItems = UserDefaults.standard.data(forKey: "Items") {
            if let decodedItems = try? JSONDecoder().decode([ExpenseItem].self, from: savedItems) {
                items = decodedItems

                return
            }
        }
        items = []
    }
}

(I'm pretty sure that is back to as it was from the class, if you don't count the commented out code.)

//
//  ContentView.swift
//  iExpense
//
//  Created by Deborah Graham on 2/8/22.
//

import SwiftUI

struct ContentView: View {
    @StateObject var expenses = Expenses()
    @StateObject var businessExpenses = BusinessExpenses()
    @StateObject var personalExpenses = PersonalExpenses()
    @State private var showingAddExpense = false

    var body: some View {
        NavigationView {
            List {
                ForEach(personalExpenses.items) { item in
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.name)
                                .font(.headline)
                            Text(item.type)
                        }
                        Spacer()
                        Text(item.amount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
                            .foregroundColor(
                                item.amount < 10.0 ? Color.green:
                                    item.amount < 100 ? Color.yellow:
                                        .red)
                    }
                }
                .onDelete(perform: removePersonalItems)
                Spacer()
                //print(expenses.items.filter(expenses.items.type == "Business"))
                ForEach(businessExpenses.items) { item in
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.name)
                                .font(.headline)
                            Text(item.type)
                        }
                        Spacer()
                        Text(item.amount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
                            .foregroundColor(
                                item.amount < 50.0 ? Color.green:
                                    item.amount < 250.0 ? Color.yellow:
                                        .red)
                    }
                }
                .onDelete(perform: removeBusinessItems)
            }
        }
        .navigationTitle("iExpense")
        .toolbar {
            Button {
                showingAddExpense = true
            } label: {
                Image(systemName: "plus")
            }
        }
        .sheet(isPresented: $showingAddExpense) {
            AddView(expenses: expenses)
        }
    }

    func removeBusinessItems(at offsets: IndexSet) {
        businessExpenses.items.remove(atOffsets: offsets)
    }
    func removePersonalItems(at offsets: IndexSet) {
        personalExpenses.items.remove(atOffsets: offsets)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

(See, the two func...to removeBusinessItems and removePersonalItems? They do not also remove the original item from expenses...if that isn't deleted, was it really deleted at all? I couldn't figure out how to get the IndexSet of the big list. That was what sent me down the make separate classes for the three lists.)

And the AddView Code, where I changed them from @ObservedObject (to mirror expenses when I was in the separate class phase) to @State (to see if that would remove the compiler error):

//
//  AddView.swift
//  iExpense
//
//  Created by Deborah Graham on 2/20/22.
//

import SwiftUI

struct AddView: View {
    @ObservedObject var expenses: Expenses

    @Environment(\.dismiss) var dismiss

    @State private var name = ""
    @State private var type = "Personal"
    @State private var amount = 0.0
    @State private var businessExpenses: Expenses
    @State private var personalExpenses: Expenses

    let types = ["Business", "Personal"]

    var body: some View {
        NavigationView {
            Form {
                TextField("Name", text: $name)

                Picker("Type", selection: $type) {
                    ForEach(types, id: \.self) {
                        Text($0)
                    }
                }

                TextField("Amount", value: $amount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
                    .keyboardType(.decimalPad)
            }
            .navigationTitle("Add new expense")
            .toolbar {
                Button("Save") {
                    let item = ExpenseItem(name: name, type: type, amount: amount)
                    expenses.items.append(item)
                    if item.type == "Business" {
                        businessExpenses.items.append(item)
                    } else {
                        personalExpenses.items.append(item)
                    }
                    dismiss()
                }
            }
        }
    }
}

struct AddView_Previews: PreviewProvider {
    static var previews: some View {
        AddView(expenses: Expenses())
    }
}

(Now, this AddView appears to be okay as it is now...the one in ContentView (as action from the .sheet(isPresented) has two errors: 'AddView' initializer is inaccessible due to 'private' protection level and Missing arguments for parameters 'businessExpenses', 'personalExpenses' in call.)

When I removed them from being @ObservedObject, do I need to "reset" something (and if so, how)? I saved and closed XCode and then opend the project again. And...what did I mess up that the initializer is inaccessible now?

And just to give you all of it, there is the ExpenseItem.swift file, too:

//
//  ExpenseItem.swift
//  iExpense
//
//  Created by Deborah Graham on 2/20/22.
//

import Foundation

struct ExpenseItem: Identifiable, Codable {
    var id = UUID()
    let name: String
    let type: String
    let amount: Double
}

Again, thanks! Sorry for the formatting snafu for a while there. I think this looks better now.

1      

Okay, that formatted strangly...did I mis-copy and can you still read it or should I try again?

1      

Do not make another copy!

You can edit a post you've made. And yes! Please edit your post. Make sure all your code is between the three back tick marks.

1      

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.