The Problem
While working on a project using SwiftUI, I discovered an issue with duplicate IDs in ForEach. I created a calendar view. I stored the dates of a month in an array, so if the first day of the month is Wednesday, Monday and Tuesday of the first week would be nil.
//...
ForEach(days, id: \.self) { day in
if let day = day {
// Code for when there is a date
} else {
Text("") // Empty if nil
}
}
//...
It works, but I found ID duplication errors in the console.
ForEach ID Explanation
ForEach in SwiftUI requires a unique ID for each element in a collection. Typically, ID is specified in the following ways:
\.self
: Uses the element itself as the ID\.id
: Uses the id property of the element as the ID- For objects implementing the
Identifiable
protocol, the ID is used automatically
However, when there are multiple nil
values, using \.self
causes all nil
values to be recognized as the same ID, resulting in duplicate ID errors.
Solutions
There are several ways to solve this.
1. Using indices to specify index as ID
Use the array’s index as the ID.
//...
ForEach(days.indices, id: \.self) { index in
if let day = days[index] {
// Code for when there is a date
} else {
Text("") // Empty if nil
}
}
//...
2. Using enumerated() to use index and value together
enumerated()
returns a Sequence type and internally provides tuples in the form of (index, value).
\.0
means to use the first element of the (index, value) tuple (index) as the ID.
//...
ForEach(Array(days.enumerated()), id: \.0) { index, item in
if let day = item {
// Code for when there is a date
} else {
Text("") // Empty if nil
}
}
//...
3. Creating unique IDs using UUID()
You can add a UUID to your data model so that each item has a unique ID.
// Implement Identifiable protocol
struct CalendarDay: Identifiable {
// Generate ID using UUID
let id = UUID()
// Date information (nil if none)
let date: Date?
// Constructor: accepts and stores Date? type
init(_ date: Date? = nil) {
self.date = date
}
}
// Example of a month's calendar data
let days: [CalendarDay] = [
CalendarDay(nil),
CalendarDay(nil),
CalendarDay(Date().startOfMonth()),
// Remaining dates...
]
// Using Identifiable object array in ForEach
// Unique identification possible without id parameter
ForEach(days) { day in
if let d = day.date {
//...
} else {
//...
}
}
Chosen Solution
I accessed the array using the indices property. I used the index value as the ID value and picked up items through that value.
//...
ForEach(days.indices, id: \.self) { index in
if let day = days[index] {
// Code for when there is a date
} else {
Text("") // Empty if nil
}
}
//...
When Using Identifiable is Good
- When array elements become more complex (when additional attributes beyond value are needed)
- When list data is likely to change (UUID ensures unique ID)
- When you want to manage IDs naturally in SwiftUI’s ForEach
Performance Considerations
- UUID() is newly created each time, so even for the same data, the ID changes when the app is re-rendered
- If data needs to be maintained, it’s better to generate UUID() and store it in the array for reuse
- If the list doesn’t change often, the indices approach is more efficient
However, I wondered if there would be duplication problems again if another ForEach in the view used the index value of the same array. Upon research, ID duplication problems can indeed occur.
If you need to use indices, combine with other values to create unique IDs
This method prevents ID conflicts that can occur when multiple ForEach use the same index range.
//...
ForEach(days.indices, id: { index in "calendar-\(index)" }) { index in
if let day = days[index] {
// Code for when there is a date
} else {
Text("") // Empty if nil
}
}
//...
This way, even if different ForEach use the same index, unique IDs like “calendar-0”, “otherView-0” are created to prevent conflicts.
Conclusion
When using ForEach in SwiftUI, it’s always important to provide unique IDs. Special care should be taken when arrays contain nil values or when multiple ForEach use the same index range. When using indices as IDs, adding a prefix to ensure uniqueness is one good approach.