Chapter 39: Swift Arrays: Indices & Bounds
Array Indices & Bounds in Swift.
Imagine we’re sitting together with a playground open. I’m going to explain this topic slowly and thoroughly — because indices and bounds are one of the places where beginners crash most often, and understanding them properly will save you many headaches.
We will cover:
- What indices really are
- Why arrays are 0-based
- How to safely access elements
- All the safe ways to work with indices
- Common crash patterns and how to avoid them
- Real-world patterns you’ll actually use in apps
Let’s go step by step.
1. The Most Important Fact: Swift Arrays are Zero-Based
The first element is always at index 0.
|
0 1 2 3 4 5 6 7 8 |
let fruits = ["Mango", "Banana", "Apple", "Orange", "Guava"] // indices: 0 1 2 3 4 |
So:
|
0 1 2 3 4 5 6 7 8 9 10 |
fruits[0] // "Mango" ← first item fruits[1] // "Banana" fruits[2] // "Apple" fruits[3] // "Orange" fruits[4] // "Guava" ← last item |
Trying to access anything outside this range crashes at runtime:
|
0 1 2 3 4 5 6 7 |
print(fruits[5]) // Fatal error: Index out of range print(fruits[-1]) // Fatal error: Index out of range |
This is not optional — it’s a deliberate design choice for performance and clarity.
2. The Three Most Important Index-Related Properties
Every Array (and most collections) has these:
|
0 1 2 3 4 5 6 7 8 9 10 |
let numbers = [10, 20, 30, 40, 50] print(numbers.startIndex) // 0 print(numbers.endIndex) // 5 ← one past the last valid index print(numbers.indices) // Range(0..<5) → 0,1,2,3,4 |
Key rule:
- Valid indices are always from startIndex (inclusive) to endIndex (exclusive)
- endIndex is never a valid index — it’s “one past the end”
3. Safe Ways to Access Elements (avoid crashes)
Way 1 – Use .first and .last (safest)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
let fruits = ["Mango", "Banana", "Apple"] print(fruits.first ?? "No first item") // Mango print(fruits.last ?? "No last item") // Apple // empty array returns nil let empty: [String] = [] print(empty.first ?? "Empty") // Empty |
Way 2 – Check with indices.contains(_:)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
let numbers = [10, 20, 30, 40, 50] let index = 3 if numbers.indices.contains(index) { print("Item at \(index): \(numbers[index])") // Item at 3: 40 } else { print("Index \(index) is out of bounds") } |
Way 3 – Use optional subscript (very clean & modern)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
extension Array { subscript(safe index: Index) -> Element? { indices.contains(index) ? self[index] : nil } } let fruits = ["Mango", "Banana", "Apple"] print(fruits[safe: 1] ?? "Missing") // Banana print(fruits[safe: 10] ?? "Missing") // Missing |
Many real projects add this extension — it’s very convenient.
Way 4 – Use dropFirst, dropLast, prefix, suffix
These return slices and never crash:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(numbers.prefix(3)) // [1, 2, 3] print(numbers.suffix(3)) // [8, 9, 10] print(numbers.dropFirst(2)) // [3, 4, 5, 6, 7, 8, 9, 10] print(numbers.dropLast(4)) // [1, 2, 3, 4, 5, 6] |
4. Real-Life Examples – how slices & indices are used
Example 1 – Pagination / “show next page”
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let allItems = Array(1...50) // imagine 50 products let pageSize = 10 let page = 3 // 1-based page number let start = (page - 1) * pageSize let end = min(start + pageSize, allItems.count) if allItems.indices.contains(start) { let pageItems = allItems[start..<end] print("Page \(page): \(pageItems)") } |
Example 2 – Recent items list (very common in UI)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let recentSearches = [ "Swift arrays", "String interpolation", "forEach vs for-in", "Array slice", "SwiftUI List" ] let recent = recentSearches.suffix(3) // last 3 items print("Recent searches:") for (index, search) in recent.enumerated() { print(" \(index + 1). \(search)") } |
Example 3 – Show first few items + “and more…”
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
let friends = ["Rahul", "Priya", "Aarav", "Sneha", "Karan", "Meera"] let previewCount = 3 if friends.count > previewCount { let preview = friends.prefix(previewCount) let moreCount = friends.count - previewCount print("Friends: \(preview.joined(separator: ", ")) + \(moreCount) more") } else { print("Friends: \(friends.joined(separator: ", "))") } |
Output (when > 3):
|
0 1 2 3 4 5 6 |
Friends: Rahul, Priya, Aarav + 3 more |
5. Very Common Beginner Mistakes & Correct Habits
| Mistake | Wrong / Dangerous code | Correct / Safe habit | Why? |
|---|---|---|---|
| Assuming slice starts at index 0 | slice[0] after array[3…] | slice.startIndex or slice.first | Slice has its own indices |
| Force-unwrapping first / last | array.first! | array.first ?? default or if let | Empty array → crash |
| Using negative indices | array[-1] | array.last or array[array.count-1] | Negative indices crash |
| Keeping large slice when original is gone | let slice = bigArray[0..<10]; bigArray = [] | let copy = Array(slice) | Slice keeps original alive in memory |
| Modifying original while holding slice | let slice = arr[0..<5]; arr.append(99) | Be aware — slice sees the change | Shared storage (can be desired or surprising) |
6. Quick Reference – Most Used Safe Index & Slice Patterns
| Goal | Recommended code | Returns type | Safe? |
|---|---|---|---|
| First item safely | array.first ?? default | Element? | Yes |
| Last item safely | array.last ?? default | Element? | Yes |
| First N items | array.prefix(N) or array[..<N] | ArraySlice | Yes |
| Last N items | array.suffix(N) | ArraySlice | Yes |
| Skip first N | array.dropFirst(N) | ArraySlice | Yes |
| Skip last N | array.dropLast(N) | ArraySlice | Yes |
| Check if index is valid | array.indices.contains(index) | Bool | Yes |
| Safe subscript | array[safe: index] (custom extension) | Element? | Yes |
| Convert slice to independent array | Array(slice) | [Element] | Yes |
7. Small Practice – Try these
- Create array of 10 numbers (1…10) → print items 3 to 7 (inclusive) using range → print them using enumerated() inside the slice
- Create array of 6 names → show only the last 3 names using suffix(3) → number them as “Recent 1: …”, “Recent 2: …”, etc.
- Create array of 8 tasks → show first 4 using prefix(4) → show “and (remaining) more…” if there are more
Paste your attempts if you want feedback or better ways!
What would you like to explore next about arrays?
- Sorting arrays (simple & custom comparators)
- Filtering, mapping, reducing in depth
- Multidimensional arrays (array of arrays)
- Array vs ArraySlice lifetime & memory
- Arrays in SwiftUI (List, ForEach, diffable data sources…)
- Or move to another topic (dictionaries, sets, strings…)
Just tell me — we’ll keep going in the same detailed, patient, teacher-like style 😊
