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

Button Actions Happen Out Of Order

Forums > SwiftUI

I'm accessing device contacts in my app, displaying in a custom list view as button cards, and then when the user taps one of the buttons for a specific contact, it simply prints the contact name and phone number associated with it.

For some reason, when I print out the Dictionary where the tapped contacts are stored, it displays the data in random orders (sometimes the first name prints first, sometimes the phone number prints first, etc.)

        var selectedCN: [String: String] = [:]

        Button(action: {
            selectedCN["firstName"] = self.firstName
            selectedCN["phoneNumber"] = self.phoneNumber
            selectedCN["lastName"] = self.lastName

            print(selectedContacts.selectedCN)

            }

This is a simplified example of my code. The Dictionary of selectedCN lives in a Class in a viewmodel file. The Button lives as a view in a struct. As I said, things print just fine, but do so in a random order when each button is tapped.

2      

Dictionary is an unordered collection, so the order of its elements is unpredictable. It's not that the actions are performed out of order, it's that when you dump the contents of selectedCN via print, they come out in whatever order the Dictionary has stored them in.

2      

Thanks @roosterboy.

I also notice that each time the button is pressed and the print statemet runs, the Dictionary only shows the latest contact information but isn't adding the details for each button tap.

Should I be using an array with an Append to ensure each button tap will add the contact details to the array and create a running list?

2      

Probably? I mean, it's kinda hard to say without seeing what you are doing in a wider scope, but I think so.

If selectedContacts is meant to hold a list of contacts that have been selected by the user, then it should be an Array, probably an Array of [String:String], so [[String:String]]. You would create selectedCN in your button action and then append it to selectedContacts.

Heck, you wouldn't even need to create selectedCN unless you need it elsewhere too. You could just do this:

selectedContacts.append(["firstName": self.firstName,
                         "lastName": self.lastName,
                         "phoneNumber": self.phoneNumber])

2      

Heres some more details. Sorry im still fairly new to Swift.

https://youtu.be/O9QOPmJXhAI

2      

It's because you've got selectedContacts as a @StateObject in every instance of ContactCardView, so each card has its own copy that is getting appended to. So when you click on "John Appleseed", he gets appended to his copy of selectedContacts and then when you click on "Kate Bell" she gets appended to her copy of selectedContacts. That's also why you see two when you click on "Kate Bell" again.

You need to create your @StateObject in the parent view of ContactCardView and pass it down—either as an @ObservedObject or as part of the environment—so that all your contact cards are working with the same instance of SelectedContacts.

@StateObject should go in the View that owns it. Think about it this way: There is one list of selected contacts that each of the cards should be updating, right? So there should only be one instance of SelectedContacts that gets passed around and shared by all the cards.

2      

I understand the copying issue you mention, thanks for all the details on that! That helps a lot.

If I were to move the empty selectedCN array out of the SelectedContacts file and into the HomeView which is the parent where the contact cards get rendered, would that make sense? Maybe i've over-complicated the logic on this as I'm trying to follow procedure of having the viewmodels handle the logic of the app.

So basically each time a contact card gets tapped, it appends to the selectedCN array within the same view, from there I guess I would pull that array back into my SelectedContacts file and start running my methods on the contacts the user tapped?

I'm still learning how property wrappers work for different uses.

Thanks again!

2      

You would put a SelectedContacts object in the HomeView as a @StateObject. Then either pass it directly to ContactCardView as an @ObservedObject or pass it as part of the environment.

So, abstractly, something like this:

struct HomeView: View {
    @StateObject var selectedContacts = SelectedContacts()

    var body: some View {
        ForEach(whatever) { item in
            ContactCardView(selectedContacts: selectedContacts, other params...)
        }
    }
}

struct ContactCardView: View {
    @ObservedObject var selectedContacts: SelectedContacts

    //other properties...

    var body: some View {
        //here you can access selectedContacts.selectedCN array
    }
}

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

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.