How to get the index of an element when iterating over an array in Swift? Let's break down this popular question
\n\nImagine: you're writing in Swift and you want not just to go through an array, but also to know the position of each element. Whether you need to modify elements by index, compare adjacent values, or simply output a numbered list — the task is the same. The question "How to iterate a loop with index and element in Swift" has gathered thousands of views on Stack Overflow. This means this topic concerns many people.
\n\nIn this article, we won't just give a ready-made answer. We'll look under the hood: we'll break down what methods exist, which one is faster, which one is safer, and why one of them — enumerated() — has become the de facto standard. Let's go!
The Problem: Why plain for-in doesn't work?
\n\nLet's say you have an array of strings:
\n\nlet fruits = ["Apple", "Banana", "Cherry", "Melon"]\n\nThe most obvious way is for-in:
for fruit in fruits {\n print(fruit)\n}\n\nThis will print the names of the fruits. But what if you need to know the index? For example: "1. Apple, 2. Banana..."? A simple for-in doesn't provide the index. You have to resort to workarounds:
var index = 0\nfor fruit in fruits {\n print("\\(index). \\(fruit)")\n index += 1\n}\n\nDoes it work? Yes. Is it elegant? No. And what if the array is empty? What if you accidentally forget to increment index? Welcome to the world of bugs. Swift offers more elegant solutions.
Method #1: The Classic — enumerated() method
\n\nThis is the most popular and recommended way. The enumerated() method returns a sequence of tuples (offset: Int, element: Element). Let's see how it works:
for (index, fruit) in fruits.enumerated() {\n print("\\(index + 1). \\(fruit)")\n}\n\nOutput:
\n1. Apple\n2. Banana\n3. Cherry\n4. Melon\n\nWhat happens under the hood?
\n\nenumerated() does not create a new array. It returns a lazy sequence of type EnumeratedSequence. This means the elements are not copied into a new data structure — they are "wrapped" on the fly. Each time the next element is accessed, the sequence simply increments a counter and returns a tuple. This is very memory efficient, especially for large collections.
An important nuance: offset always starts at 0. If you need an index starting from 1 (like in list numbering), simply add one. Swift doesn't do this for you — this is a conscious decision by the language developers to maintain consistency with array indexing.
enumerated() with forEach
\n\nThe forEach method also supports enumerated():
fruits.enumerated().forEach { (index, fruit) in\n print("\\(index). \\(fruit)")\n}\n\nThe difference between for-in and forEach is important: inside forEach, you cannot use break or continue. If you need to exit the loop early — use for-in.
Method #2: Indexing via Range
\n\nAn old but still encountered way is to iterate over indices:
\n\nfor index in 0..<fruits.count {\n print("\\(index). \\(fruits[index])")\n}\n\nIt seems simple. But there are pitfalls:
\n\n- \n
- Safety: If the array changes during iteration (for example, in multithreaded code),
fruits.countmay become outdated, and you will go out of the array bounds. \n - Readability: The code looks more "low-level". Other developers will spend an extra second to understand what you are doing. \n
- Performance: Accessing by index
fruits[index]is an O(1) operation for arrays, but for other