SwiftUI: Why does ForEach need an ID?

Viewed 861

Im using a ForEach loop in my SwiftUI View and I am getting strange warnings.

It works fine like this:

ForEach(0..<7) { i in
    // do something
}

Then I changed 7 to a constant:

let numberOfElements = 7
ForEach(0..<numberOfElements) { i in
    // do something
}

And got the following warning:

Non-constant range: argument must be an integer literal

I googled an found the following solution which works:

let numberOfElements = 7
ForEach(0..<numberOfElements, id:\.self) { i in
    // do something
}

However, I have no idea why it works. Why do I have to give an ID to the ForEach loop, and what is the ID for?

1 Answers
ForEach(0..<numberOfElements) { i in
    // do something
}

The reason why using the above ForEach init pops the using literal value warning is because SwiftUI doesn't expect to re-render anything when using the Range<Int> init method. This is a documented requirement / feature. The exceptions are the init methods with id:.

A hashable id matters in SwiftUI as well as in many other view-tree based frameworks like React is because these UI frameworks needs to use these ids to track updates for views inside the ForEach or any other "Container Views", to help the framework achieve usable performance. If you want to dig deeper, take a look at this WWDC video: Demystify SwiftUI.

Related