enumerated() on the array and destructure the pair in the loop: for (index, element) in list.enumerated(). The global enumerate(list) function you may remember from Swift 2 no longer exists, and the pairs you get back are labelled (offset:element:).Why this happens
You are probably porting Python’s enumerate() or an old Swift 2 snippet, so the compiler stops at enumerate(list) with cannot find 'enumerate' in scope. Swift removed the free-standing function and moved the behaviour onto Sequence itself, where it is called enumerated(). It returns an EnumeratedSequence whose elements are two-member tuples.
There is a second, quieter trap. The first value in each tuple is an offset counted from zero over the sequence you call it on — it is not the collection’s Index. For a plain Array the two coincide, which is why most code works and hides the difference. Apply enumerated() to an ArraySlice, to dropFirst() or to reversed(), and the numbers restart at 0 while the underlying indices do not.
Finally, modern Swift labels the tuple members (offset:element:). Writing for (index, element) in ... is just your own naming for those positions — it works, but it does not rename the tuple itself, so code that reads pair.index will not compile.
How to fix it
1. Use enumerated() with a for loop
This is the direct replacement for enumerate(list) and covers almost every case:
let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.enumerated() {
print("Item \(index): \(element)")
}
// Item 0: Car
// Item 1: Bike
// Item 2: Plane
// Item 3: Boat
The parentheses around index, element are destructuring the tuple; leaving them out gives you the whole pair instead:
for pair in list.enumerated() {
print(pair.offset, pair.element)
}
Use the named form when the line is long or when you pass the pair to a helper function. Use the destructured form when you genuinely only need the two values.
2. Build a collection of pairs in one step
A common anti-pattern is an empty array plus a loop appending tuples. enumerated() is already a sequence, so convert it:
let numbers = [Int](1...5)
let pairs = Array(numbers.enumerated())
// [(offset: 0, element: 1), (offset: 1, element: 2), ...]
Mapping is just as short and keeps the transformation explicit:
let indexToValue = numbers.enumerated().map { [$0.offset: $0.element] }
// [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]
If you actually want a dictionary keyed by position, skip the intermediate array:
let byIndex = Dictionary(uniqueKeysWithValues: numbers.enumerated())
// [0: 1, 1: 2, 2: 3, 3: 4, 4: 5]
3. Use indices when you need the real index
When the collection is not a plain array — or when you have already transformed it — iterate indices and subscript instead. The numbers you get are guaranteed to be valid subscripts:
let slice = list[1...]
for i in slice.indices {
print(i, slice[i]) // 1 Bike, 2 Plane, 3 Boat
}
zip does the same without subscripting and reads well when you also want the element:
for (i, element) in zip(slice.indices, slice) {
print(i, element)
}
4. Do not confuse offsets with indices after reversed() or dropFirst()
This is the bug that enumerated() quietly introduces:
let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.dropFirst().enumerated() {
print(index, element) // 0 Bike, 1 Plane, 2 Boat
}
Those offsets are 0, 1, 2 even though the elements live at 1, 2, 3. If you need the original positions, zip the original indices and drop the same prefix:
for (index, element) in zip(list.indices, list).dropFirst() {
print(index, element) // 1 Bike, 2 Plane, 3 Boat
}
reversed() behaves the same way: list.reversed().enumerated() counts 0 upward while walking backwards. That is fine for display, wrong for anything that maps back to the source array.
5. Prefer a for loop over forEach when you need control flow
forEach accepts the same pairs, but you cannot break, continue or return from the enclosing function inside it, and a return in the closure only ends that iteration:
list.enumerated().forEach { index, element in
print(index, element)
}
Use it for one-line side effects such as logging. Reach for a real for loop as soon as the body needs early exit.
How to prevent it
- Search your project for
enumerate(and replace it with.enumerated(); the old global function does not exist in any Swift 3 or later toolchain. - Treat the first tuple value as an offset, not an index. If the receiver is a slice,
dropFirst(),prefix()orreversed(), decide explicitly which one you need. - Name the loop variables
offsetrather thanindexwhen you are iterating anything other than a plain array — future readers then see the caveat in the code. - When you need positions that index back into the original storage, use
indicesorzip(collection.indices, collection)instead. - Do not use
forEachfor anything withbreak,continueor a throwing early exit; use aforloop.
FAQ
What replaced enumerate() in Swift?
enumerated(), a method on Sequence. The global function enumerate(_:) was removed in Swift 3, so enumerate(list) no longer compiles. Call list.enumerated() and destructure the (offset:element:) tuples in your loop.
Why does pair.index fail to compile on an enumerated tuple?
The tuple members are labelled offset and element. Your for (index, element) in ... binding only creates local names for those positions, it does not rename the tuple. Access pair.offset and pair.element, or destructure in the loop header.
Does enumerated() start at zero if the array is a slice?
Yes. It numbers the elements of whatever sequence you call it on, starting at 0, regardless of the original indices. For an ArraySlice from list[1...] you get offsets 0, 1, 2 while the elements sit at indices 1, 2, 3 — use slice.indices when you need the real ones.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
