WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

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?

1      

+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.

   

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.