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

SOLVED: list isn't updating when I add a new item

Forums > SwiftUI

Hi everyone, I used same idea of iExpense project exactly but without .sheet method, The list isn't updating when I add new item from AddView.

struct MainView: View {
    var body: some View {
        TabView {
            ContentView() 
                .tabItem {
                    Image(systemName: "1.lane")
                    Text("One")
                }
            AddView(expenses: Expenses())
                .tabItem {
                    Image(systemName: "2.lane")
                    Text("Two")
                }
        }
    }
}

other codes same as iExpense project....

How to fix that?

project in github: https://github.com/mahoozi97/iExpense-project

Thanks in advance

2      

Hi @mahoozi97!

Here you can do as follows. Comments are added so I guess it is clear what has been changed.

Changes to MainView:

struct MainView: View {
    // Create your expenses object in the main view
    @StateObject var expenses = Expenses()
    // This is necessary for switching between tabs
    @State var selectedTab = 0

    var body: some View {
        // Use selection initializer for TabView
        TabView(selection: $selectedTab) {
            ContentView()
                .tabItem {
                    Image(systemName: "1.lane")
                    Text("One")
                }
                // we assign tag to programmatically switch between tabs
                .tag(1)
            // Also we pass selectedTab to AddView so that we can go back to ContentView programmatically
            AddView(selectedTab: $selectedTab)
                .tabItem {
                    Image(systemName: "2.lane")
                    Text("Two")
                }
                // we assign tag to programmatically switch between tabs
                .tag(2)
        }
        // Inject expenses object into environment so that ContentView and AddView can access it
        .environmentObject(expenses)
    }
}

Changes to ContentView:

struct ContentView: View {
    // This is how we can access expenses object in the environment
    @EnvironmentObject var expenses: Expenses

    private var totalAmount: Double {
            expenses.items.reduce(0.0) { partialResult, item in
                partialResult + item.amount
            }
        }

    var body: some View {
        NavigationView {
            List {
                ForEach(expenses.items, id: \.id) { item in
                    HStack {
                        VStack(alignment: .leading) {

                            Text(item.name)
                                .font(.headline)
                            Text(item.type)
                        }

                        Spacer()

                        Text(item.amount, format: .currency(code: Locale.current.currency?.identifier ?? "BD"))
                            .foregroundColor(item.amount <= 10 ? .green : item.amount <= 100 ? .orange : .red)

                    }
                }
                .onDelete(perform: removeItem)

                HStack {
                    Text("Total amount")
                    Spacer()
                    Text("\(totalAmount.formatted(.currency(code: Locale.current.currency?.identifier ?? "BD")))")
                }
            }
            .navigationTitle("iExpense")
        }
    }

    func removeItem(at offsets: IndexSet) {

        expenses.items.remove(atOffsets: offsets)
    }
}

// Pay attention that you need to add .environmentObject in previews to make it work
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(Expenses())
    }
}

Changes to AddView:

struct AddView: View {
    // This is how we can access expenses object in the environment
    @EnvironmentObject var expenses: Expenses

    @State private var name = ""
    @State private var type = "personal"
    @State private var amount = 0.0

    // Here we pass selectedTab binding
    @Binding var selectedTab: Int

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

    @Environment(\.dismiss) var dismiss

    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.currency?.identifier ?? "BD"))
                    .keyboardType(.decimalPad)
            }
            .navigationTitle("Add new expense")
            .toolbar {
                Button("Save") {

                    let item = ExpenseItem(name: name, type: type, amount: amount)

                    expenses.items.append(item)
                    // This is how we jump back to ContentView upon pressing save
                    selectedTab = 1
                }
            }
        }
    }
}

// Pay attention that you need to add .environmentObject in previews to make it work
struct AddView_Previews: PreviewProvider {
    static var previews: some View {
        AddView(selectedTab: .constant(2))
            .environmentObject(Expenses())
    }
}

2      

ADDITION:

You might also want to add this to your save button in AddView I suppose :)

 Button("Save") {

    let item = ExpenseItem(name: name, type: type, amount: amount)
    expenses.items.append(item)
    // This is how we jump back to ContentView upon pressing save
    selectedTab = 1

    // Clear the textFields to default values
    name = ""
    amount = 0.0
}

PS It is not a must to use .environmentObject to make it all work

you can also pass expenses object to the views directly but for that you will need to pass object via @ObservedObject like:

struct AddView: View {
@ObservedObject var expenses: Expenses

...
}

struct ContentView: View {
@ObservedObject var expenses: Expenses

...
}

2      

Thanks @ygeras, my issue is solved.

I have other question about deleted items, if I delete item and stop simulator Immediately then run again, sometimes the app recovers that deleted item. I asked here rather than share new topic.

2      

If not mistaken, UserDefaults takes some time to save data, so when you kill you app immediately after deleting an item, it might not have enough time to write data to storage. Besides UserDefaults is recommended to use for small pieces of data like settings etc. not for large set or arrays, so in reality you might not use it for real apps in this context. But for educational purposes it is more than enough.

2      

Thank you very much @ygeras

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!

Reply to this topic…

You need to create an account or log in to reply.

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.