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

Possible race condition with @State? (newbie question)

Forums > SwiftUI

I'm working through the 100 days of SwiftUI. I'm working on the iExpense app and I'm attempting to make it so that when you click on an item's "edit" button, you can visit the AddItem view and rather than make a new expense, you'll edit the existing one. I'm doing this by tracking an optional editId. What I noticed is that if I launch the app and restart it and then attempt to edit an existing item that was recalled from UserData, it for some reason thinks the editId is nil. At first I thought maybe my expense id values were not getting stored correctly or getting out of sync but I added a number of print statements and verified they are in fact in sync. What's odd though, is if I add this statement to the top of ContentView's body

if let editId = self.editId {
  print("edit id: \(editId)")
}

And then immediately return my view, it always works. That is to say, it always seems to recognize the correct editId. However, the second I remove that, it no long works. Here's the complete code for ContentView for context (note: the trailing closure for .sheet() is where the editId is sometimes right/wrong)

import SwiftUI

struct ContentView: View {
    @ObservedObject var expenses: Expenses = Expenses()
    @State private var showingAddExpense = false
    @State private var editId: UUID?

    func removeItems(at offsets: IndexSet) {
        expenses.items.remove(atOffsets: offsets)
    }

    var body: some View {
        /*
         * For some reason, if i I don't attempt to unwrap the self.editId
         * here then initially when you try to edit an existing item,
         * it will be treated as nil. :shrug:
         */
        if let editId = self.editId {
            print("edit id: \(editId)")
        }

        return NavigationView {
            List {
                ForEach(expenses.items) { item in
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.name)
                                .font(.headline)
                            Text(item.type)
                            Text("\(item.id)")
                                .font(.footnote)
                        }

                        Spacer()
                        Text("$\(item.amount)")
                        Button("Edit") {
                            self.showingAddExpense = true
                            self.editId = item.id
                        }
                    }
                    .padding()
                    .background(item.amount > 100 ? Color.red : item.amount > 50 ? Color.yellow : item.amount > 10 ? Color.green : nil)
                }
                .onDelete(perform: removeItems)
            }
            .navigationBarTitle("iExpense")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    HStack {
                        Button(action: {
                            self.editId = nil
                            self.showingAddExpense = true
                        }) {
                            Image(systemName: "plus")
                        }
                    }

                }

                ToolbarItem(placement: .navigationBarLeading) {
                    EditButton()
                }
            }
        }
        .sheet(isPresented: $showingAddExpense) {
            // This is where self.editId will be nil if I do not include the if expression at the top
            AddView(expenses: self.expenses, editId: self.editId)
        }

    }
}

If you need additional context or other parts of the code, I'm happy to paste it in

2      

Did you already solve this?

It looks like you need to reverse the order of the two statements in this closure:

Button("Edit") {
                            self.showingAddExpense = true
                            self.editId = item.id
                        }

The current order sets showingAddExpense to true and therefore displays the sheet without setting the value of editId.

2      

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.