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

SOLVED: Absolute Noob here, can anyone help me trouble shoot my button and array woes?

Forums > SwiftUI

I have been trying to make a very basic bill tracking app as my first solo project (in SwiftUI) after finishing SwiftPlaygrounds 1-3. I have gotten the views mostly setup for a skeleton, but I keep running into walls trying to save data input from text fields into an array and then display those array objects in a list.

(Don't mind I have the notifications functionality and stuff "//" out until I get the core working)

I need to have input of a bill name and amount, so I made a struct called Bill Entry. When I try to append an instance of that struct into the array "entries" it just throws an error that it's too ambigous. When I use the $var version it throws an error saying I can't use binded entries and/or partial mutations.

I am very new and struggling not to just give up as everything I have googled/tried seems to throw a different error, and I am really not sure what the heck I am doing so wrong to just make something simple like this. I have stepped back and worked through some basic task list apps and the like via youtube tutorials, and those seem to work, but I cannot get my project to work at all on this front.

Anyone able to help, but more importantly explain the WHY on this so I can learn more and not just continue bashing my head into the magical wall of errors?

Any/All help is appreciated; here's my working code (I think I formatted it right?):


import SwiftUI

struct ContentView: View {

    //newly added code
    struct BillEntry : Identifiable {
        let id = UUID()
        var billName: String
        var billAmount: Double
    }

    @State var bill : String = ""
    @State var amount : Double = 0.00
    @State var placeholder = ""
    @State var entries = [BillEntry].self
    @State var isRecurring : Bool = true
    @State var notificationsEnabled : Bool = false
    @State var previewIndex = 0
    var previewOptions = ["Always", "When Unlocked", "Never"]

    var body: some View {
        NavigationView {
            Form {
                Section(header:Text ("BILL ENTRY")) {
                    TextField ("Bill Name / Deposit Name", text: $bill)
                    TextField("Amount ", text: $placeholder)
                    Toggle(isOn: $isRecurring) {
                        Text("Is this recurring?")
                    }
                }

                Section {
                    Button(action: {
                       //tried this first, didnt work -- type of expression is ambigous without more context//
                   // entries.append(BillEntry(billName: bill, billAmount: amount))

                        ///tried this next, didn't work
                      // var billItem = BillEntry(billName: $bill, billAmount: $amount)
                       // entries.append(billItem)

                        //This didn't work either
                    //    let named = $bill
                   //     let amounted = $amount
                    //    let billItem = BillEntry(billName: named, billAmount: amounted)
                   //     entries.append(billItem)
                        //This also didn't work
                        //var tempBill = BillEntry(billName: bill, billAmount: amount)
                      //  entries.append(tempBill)
                        print("HELP ME THIS NEVER WORKS?!")
                    }) {
                        Text("Add Bill")
                            .foregroundColor(Color.primary)
                        }
                    }

              //  Section(header: Text("NOTIFICATIONS")) {
               //     Toggle(isOn: $notificationsEnabled) {
                //        Text("Enabled")
                //    }
                //    Picker(selection: $previewIndex, label: Text("Show previews")) {
                //        ForEach(0 ..< previewOptions.count) {
                 //           Text(self.previewOptions[$0])
                  //      }
                   // }
               // }

                Section(header: Text("CURRENT BILLS")) {
                    List {
                        Text("Maybe someday an array of values from entries will go here, can't get that to work either")
                      //  ForEach entry in entries {
                          //  Text(entry)
                        //}
                        }
                    }
                }
            .padding(.leading)
            .navigationTitle("Bills")
            }
            }
        }

3      

Take a look at this:

struct ContentView: View {

    //newly added code
    struct BillEntry : Identifiable {
        let id = UUID()
        var billName: String
        var billAmount: Double
    }

    @State var bill : String = ""
    @State var amount : Double = 0.00
    @State var amountTxt: String = ""
    @State var entries = [BillEntry]()                          //1
    @State var isRecurring : Bool = true
    @State var notificationsEnabled : Bool = false
    @State var previewIndex = 0
    var previewOptions = ["Always", "When Unlocked", "Never"]

    var body: some View {
        NavigationView {
            Form {
                Section(header:Text ("BILL ENTRY")) {
                    TextField ("Bill Name / Deposit Name", text: $bill)
                    TextField("Amount", text: $amountTxt)
                        .onChange(of: amountTxt) { _ in                          //2
                            amount = Double(amountTxt) ?? 0.0
                        }
                    Toggle(isOn: $isRecurring) {
                        Text("Is this recurring?")
                    }
                }

                Section {
                    Button(action: {
                        entries.append(BillEntry(billName: bill, billAmount: amount))
                    }) {
                        Text("Add Bill")
                            .foregroundColor(Color.primary)
                    }
                }

                  Section(header: Text("NOTIFICATIONS")) {
                     Toggle(isOn: $notificationsEnabled) {
                        Text("Enabled")
                    }
                    Picker(selection: $previewIndex, label: Text("Show previews")) {
                        ForEach(previewOptions, id: \.self) { opt in
                           Text(opt)
                      }
                 }
                 }

                Section(header: Text("CURRENT BILLS")) {
                    List {
                        ForEach(entries) { entry in                                             //3
                            Text("\(entry.billName) = \(entry.billAmount)")
                        }
                    }
                }
            }
            .padding(.leading)
            .navigationTitle("Bills")
        }
    }
}

Three points:

  1. Your error here was in using [BillEntry].self. That says that entries is the type of [BillEntry] but what you want is an array of BillEntrys. Fix that one line and most of your code will work just fine.

  2. This onChange handler is to work around an issue with TextField. Ideally you would want to use the init method with value and formatter parameters but it doesn't update the state vars in real time.

  3. Since you already have your BillEntry struct conform to Identifiable, you can loop through them very easily.

4      

Thank you! That fixed it, I am still trying to learn all this, so I appreciate you both showing how its solved and telling WHY that solved it, you rock!

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.