Swift
Two-dimensional array
Programming
Swift arrays
iOS development

Two-dimensional array in Swift

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A two-dimensional array in Swift is an array of arrays — [[T]]. Swift does not have a built-in 2D array type, so you create one by nesting arrays. The outer array represents rows and each inner array represents a column. Initialization uses Array(repeating:count:) nested two levels deep, and access uses double subscript notation like grid[row][col]. For performance-critical code, consider a flat array with manual row/column indexing.

Creating a 2D Array

Literal Initialization

swift
1// 3x3 grid
2var grid = [
3    [1, 2, 3],
4    [4, 5, 6],
5    [7, 8, 9]
6]
7
8print(grid[0])     // [1, 2, 3] — first row
9print(grid[1][2])  // 6 — row 1, column 2

Using repeating:count:

swift
1// 4 rows x 5 columns, filled with zeros
2var matrix = Array(repeating: Array(repeating: 0, count: 5), count: 4)
3
4print(matrix.count)        // 4 (rows)
5print(matrix[0].count)     // 5 (columns)
6
7// Modify a cell
8matrix[2][3] = 42

Empty 2D Array

swift
1// Empty, then build up
2var grid: [[Int]] = []
3
4grid.append([1, 2, 3])
5grid.append([4, 5, 6])
6print(grid)  // [[1, 2, 3], [4, 5, 6]]

Type-Annotated

swift
1// Explicit type
2let board: [[String]] = [
3    ["X", "O", "X"],
4    ["O", "X", "O"],
5    ["X", "O", "X"]
6]

Accessing and Modifying Elements

swift
1var grid = [
2    [1, 2, 3],
3    [4, 5, 6],
4    [7, 8, 9]
5]
6
7// Read
8let value = grid[1][2]  // 6
9
10// Write
11grid[0][0] = 100
12
13// Replace an entire row
14grid[2] = [70, 80, 90]
15
16// Append a new column to each row
17for i in 0..<grid.count {
18    grid[i].append(0)
19}
20// Each row now has 4 elements

Iterating Over a 2D Array

swift
1let grid = [
2    [1, 2, 3],
3    [4, 5, 6],
4    [7, 8, 9]
5]
6
7// Nested for loops
8for row in 0..<grid.count {
9    for col in 0..<grid[row].count {
10        print("[\(row)][\(col)] = \(grid[row][col])")
11    }
12}
13
14// Using enumerated()
15for (rowIndex, row) in grid.enumerated() {
16    for (colIndex, value) in row.enumerated() {
17        print("(\(rowIndex), \(colIndex)) = \(value)")
18    }
19}
20
21// Flat iteration
22for row in grid {
23    for value in row {
24        print(value, terminator: " ")
25    }
26    print()
27}
28// 1 2 3
29// 4 5 6
30// 7 8 9

Common Operations

Transpose

swift
1func transpose<T>(_ matrix: [[T]]) -> [[T]] {
2    guard let firstRow = matrix.first else { return [] }
3    return firstRow.indices.map { col in
4        matrix.map { $0[col] }
5    }
6}
7
8let original = [[1, 2, 3], [4, 5, 6]]
9let transposed = transpose(original)
10// [[1, 4], [2, 5], [3, 6]]
swift
1func find<T: Equatable>(value: T, in grid: [[T]]) -> (row: Int, col: Int)? {
2    for (r, row) in grid.enumerated() {
3        if let c = row.firstIndex(of: value) {
4            return (r, c)
5        }
6    }
7    return nil
8}
9
10if let pos = find(value: 5, in: grid) {
11    print("Found at (\(pos.row), \(pos.col))")  // (1, 1)
12}

Flatten

swift
let grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flat = grid.flatMap { $0 }
// [1, 2, 3, 4, 5, 6, 7, 8, 9]

Jagged Arrays (Uneven Rows)

Swift's [[T]] supports rows of different lengths:

swift
1var jagged: [[Int]] = [
2    [1],
3    [2, 3],
4    [4, 5, 6],
5    [7, 8, 9, 10]
6]
7
8print(jagged[0].count)  // 1
9print(jagged[3].count)  // 4
10
11// Safe access
12func safeGet(_ grid: [[Int]], row: Int, col: Int) -> Int? {
13    guard row >= 0, row < grid.count,
14          col >= 0, col < grid[row].count else { return nil }
15    return grid[row][col]
16}

Performance-Optimized Flat Array

For large matrices, a flat array with manual indexing avoids the overhead of nested arrays:

swift
1struct Matrix<T> {
2    let rows: Int
3    let cols: Int
4    var storage: [T]
5
6    init(rows: Int, cols: Int, defaultValue: T) {
7        self.rows = rows
8        self.cols = cols
9        self.storage = Array(repeating: defaultValue, count: rows * cols)
10    }
11
12    subscript(row: Int, col: Int) -> T {
13        get { storage[row * cols + col] }
14        set { storage[row * cols + col] = newValue }
15    }
16}
17
18var m = Matrix(rows: 3, cols: 3, defaultValue: 0)
19m[1, 2] = 42
20print(m[1, 2])  // 42

This uses contiguous memory, which is significantly faster for large matrices due to better cache locality.

Common Pitfalls

  • Array(repeating:count:) shares references for reference types: Array(repeating: [Int](), count: 3) creates three independent arrays because [Int] is a value type. But for reference types (classes), all rows would share the same instance. Use a loop or map to create independent instances.
  • Out-of-bounds access: Swift arrays crash on out-of-bounds access. Always check row < grid.count && col < grid[row].count before accessing grid[row][col], especially with jagged arrays.
  • Modifying during iteration: Modifying grid[row][col] while iterating with for row in grid creates a copy of the row. Use index-based iteration (for i in 0..<grid.count) if you need to modify in place.
  • Assuming uniform row lengths: Unlike C or Java fixed-size arrays, Swift's [[T]] does not enforce that all inner arrays have the same length. A grid.append([1, 2]) followed by grid.append([3]) is valid but makes column-based access unsafe.
  • Performance of nested arrays: Each inner array is a separate heap allocation. For large matrices (1000x1000+), use a flat [T] with manual row * cols + col indexing for better cache performance and fewer allocations.

Summary

  • Create 2D arrays with [[T]] syntax or Array(repeating: Array(repeating: value, count: cols), count: rows)
  • Access elements with double subscript grid[row][col]
  • Use enumerated() for indexed iteration and flatMap for flattening
  • Swift does not enforce uniform row lengths — all inner arrays can have different sizes
  • For large matrices, use a flat array with row * cols + col indexing for better performance

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.