Solving ForEach ID Duplication Problems in SwiftUI


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.