GO FURTHER, FASTER: Try the Swift Career Accelerator today! >>

SOLVED: DAY 16: Why is it written ForEach(students, id: \.self) { instead of ForEach(students, id: self) {

Forums > 100 Days of SwiftUI

Why is it written ForEach(students, id: \.self) instead of ForEach(students, id: self) ?

   

Because \.self refers to the self property of students. Just using self without the \. doesn't tell you what self refers to because there is no surrounding context.

   

So how does that backslash work? It seems to be replaced by "students" but how? Google omits "\" from searches, thank you so much!

   

That's a key path.

A key path is a way to refer to a property of a type. So \Person.firstName would refer to the firstName property of a Person type. When the type context is known, you can omit the type name: just \.firstName will suffice in a context that expects a Person.

\.self is a special key path: The path can refer to self to create the identity key path (\.self). The identity key path refers to a whole instance, so you can use it to access and change all of the data stored in a variable in a single step.

In SwiftUI's ForEach, the items you are looping through have to be uniquely identifiable so that SwiftUI can track them properly. You have to indicate which property of the items being looped through is used to provide that unique identification.

So, something like:

ForEach(people, id: \.fullName)

tells ForEach that each item in people can be uniquely identified by its fullName property.

Using \.self as the id tells SwiftUI that each item can be uniquely identified just by its own intrinsic value, i.e., by itself. So, for instance, an integer doesn't need a property to uniquely identify itself, 9 and 3 and 437 are just inherently identified by their very own value. Likewise with strings: Do or do not. There is no try. is uniquely identified by itself, as is Fourscore and seven years ago or What, me worry?

So to take the example from your post, ForEach(students, id: \.self) says that each item in students can be uniquely identified by its intrinsic value rather than by any particular property of the item.

Make sense?

2      

+500 points for Alfred E Neuman reference!

1      

So \ refers to the immediately-precedent object. In the example below, \ refers to people.

ForEach(people, id: \.fullName)

Cool. But Swift has SO MUCH syntactic sugar!

   

So \ refers to the immediately-precedent object. In the example below, \ refers to people.

Sort of.

The method signature of this particular version of ForEach looks like this:

init(
    _ data: Data,
    id: KeyPath<Data.Element, ID>,
    @ViewBuilder content: @escaping (Data.Element) -> Content
)

We can see from the notes on this page that the data parameter has to be something that conforms to the RandomAccessCollection protocol (like, say, an array). RandomAccessCollection has a generic type parameter called Element that refers to the type of whatever is contained in the collection.

You can see from the above method signature that id is a key path pointing to a property on the type Data.Element that is generically labelled ID.

So the \ doesn't refer to the immediately-precedent object, which is a collection of some things. It refers to the some thing that is contained in the collection.

To use our example again, let's presume we have a struct called Person that has a property fullName by which every person can be uniquely identified (nevermind that this would actually be a terrible way of doing it):

struct Person {
  let firstName: String
  let lastName: String
  var fullName: String {
     "\(firstName) \(lastName)"
  }
}

And we have a collection of Person items that we store in an array called people:

let people = [Person]()

And we want to loop through people and display each Person in a neat list or something:

ForEach(people, id: \.fullName) { person in
  //blah blah blah display stuff
}

What this tells us (looking back up at our method signature) is that our Data type is [Person], an array being something that conforms to the RandomAccessCollection protocol. Which means that Data.Element (the type of thing contained in Data) is Person and the key path supplied to the id parameter is a KeyPath<Person, fullName>, meaning "take a Person object and refer to its fullName property".

So to rephrase your initial statement:

\ refers to the type of the items in the immediately-precedent collection. In the example below,\ refers to Person.

   

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 and A/B test your entire paywall UI 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.