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

Day 16: Confusion about IDs that can be used to identify SwiftUI views?

Forums > 100 Days of SwiftUI

Hey all!

I've been working through Day 16 of 100 Days and got to the lesson about Creating Views in a Loop.

In particular, I was curious about the purpose of the ID parameter and how a view could behave when it's misused. From the tutorial, Paul writes that the "id: \.self part is important. This exists because SwiftUI needs to be able to identify every view on the screen uniquely, so it can detect when things change"

I tweaked the code provided in the lesson by swapping \.self with \.count. My understanding is that this tells SwiftUI to identify the strings with their lengths, rather than the Strings themselves.


struct ContentView: View {

    let students = ["Harry", "Hermione", "Ron"]
    @State private var selectedStudent = "Harry"

    var body: some View {
        NavigationView {
            Form {
                Picker("Select your student", selection: $selectedStudent) {
                    ForEach(students, id: \.count) {
                        Text($0)
                    }
                }
            }
        }
    }
}

When running this code, the Picker misbehaves in that it doesn't appear to allow different selections. I'm having trouble wrapping my mind around why this is the case.

At a glance, the count values for the strings provided above should all be unique (5, 8, and 3 respectively). Skimming the documentation about forEach for lists, my understanding is that ID just needs to be hashable. Since the .count property of strings is an Integer, and integers are hashable, would't this criteria be met? What else could explain why the views are not created or behaving as intended?

Apologies ahead of time if my question is framed poorly. I'm not too familiar with the underlying behavior of SwiftUI views, so wondering if someone more experienced could shed light on what could be going on here.

TL;DR Why does the picker misbehave if we choose to use \.count as the identifier above, rather than \self or another string property like \.debugDescription?

Any pointers or suggestions for how to further investigate this would be greatly appreciated. Thank you!

2      

You kind of get a hint at the issue when you say:

TL;DR Why does the picker misbehave if we choose to use \.count as the identifier above, rather than \self or another string property like \.debugDescription?

The key there is "another string property".

The type of the variable you use to track the selection must be the same as the type of the id used to identify the items.*

selectedStudent is a String

When you use ForEach(students, id: \.self), the id property use each item as its own identifier. Since the items in students are Strings, then id is a String.

But when you use ForEach(students, id: \.count), then id is an Int (i.e. the length of each String in students).

So since the two types don't match, the selection in your Picker doesn't work.


* Technically, it's the .tag property of each View inside your ForEach that has to match the type of the selection variable. But the default .tag property is just the id, so unless you explicitly specify a .tag, it's essentially the same thing.

You can see it by doing this:

ForEach(students, id: \.count) {
    Text($0).tag($0)
}

2      

Wow that's a super subtle detail about the types having to match, thank you @roosterboy for the reply.

I can see it's pretty buried in the documentation about View Tags...

A ForEach automatically applies a default tag to each enumerated view using the id parameter of the corresponding element. If the element’s id parameter and the picker’s selection input have exactly the same type, you can omit the explicit tag modifier. To see examples that don’t require an explicit tag, see Picker.

Wondering how I could've potentially arrived at this detail through a bit more digging, or if it just comes with experience / time working with SwiftUI?

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.