ios-swift
/

Swift Collections – Mastering Arrays, Sets, and Dictionaries

Last Sync: Today

On this page

7
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

ios-swift

Swift Collections – Mastering Arrays, Sets, and Dictionaries

Choosing the Right Collection for Your Data

Swift provides three primary collection types. Choosing the wrong one leads to messy code or performance issues. Use this rule of thumb:

  • Array: You care about order (list of dashboard widgets, chat messages).
  • Set: You care about uniqueness and fast lookups (user IDs in a room, tags on a post).
  • Dictionary: You need to look up values by an identifier (user profiles by ID, configuration flags).

For Flutter developers: Array = List<T>, Set = Set<T>, Dictionary = Map<K,V>.

  1. Arrays – Ordered, Indexed, Duplicate-Allowed

Arrays are zero-indexed, ordered collections that can contain duplicate values. They are optimized for sequential iteration and access at the end of the array. Insertion or removal at the front is O(n) because elements shift.

SWIFTRead-only
1
// Explicit typing vs. type inference
var dashboardWidgets: [String] = ["RevenueChart", "UserStats", "RecentActivity"]
var scores = [95, 87, 102]  // Inferred as [Int]

// Adding elements
scores.append(98)          // Add to end (O(1) amortized)
scores.insert(100, at: 1)  // Insert at index 1 (O(n))

// Safe access using if let
if let firstScore = scores.first {
    print("High score: \(firstScore)")
}

// Removing elements
let removed = scores.removeLast()  // Remove from end
scores.removeAll { $0 < 90 }       // Remove scores below 90

// Useful properties
print("Count: \(scores.count), Is Empty: \(scores.isEmpty)")

  1. Dictionaries – Fast Key-Based Lookups

Dictionaries store unordered key-value pairs. Keys must be Hashable (String, Int, etc.). Lookup, insertion, and deletion are all O(1) on average – making them perfect for caches or data stores. Access always returns an Optional because the key might not exist.

SWIFTRead-only
1
// Dictionary with String keys and Int values
var userSessions: [String: Int] = [
    "user_001": 3,
    "user_002": 7
]

// Safe update using subscript
userSessions["user_003"] = 1  // Adds new entry

// Update only if key exists
userSessions["user_001"]? += 1  // Now becomes 4

// Provide a default value to avoid Optional
let sessionsForUser004 = userSessions["user_004", default: 0]

// Removing entries
userSessions["user_002"] = nil

// Iterating over keys and values
for (userId, sessionCount) in userSessions {
    print("User \(userId) has \(sessionCount) sessions")
}

// Get all keys or values as arrays
let allUserIds = Array(userSessions.keys)

  1. Sets – Uniqueness and Fast Membership Testing

A Set is an unordered collection of unique values. Its superpower is contains() – O(1) vs O(n) for an Array. Use Sets for tag systems, permission flags, or deduplicating data. Elements must be Hashable.

SWIFTRead-only
1
// Creating a Set from an array literal
var activeFilters: Set<String> = ["price:low", "brand:apple", "inStock"]

// Insert (duplicates are silently ignored)
activeFilters.insert("price:low")  // No effect

// Checking membership – super fast!
if activeFilters.contains("brand:apple") {
    print("Apple products only")
}

// Removing
activeFilters.remove("inStock")

// Set operations – great for comparing collections
let premiumFilters: Set = ["brand:apple", "price:high", "expressShipping"]
let combined = activeFilters.union(premiumFilters)        // All filters
let common = activeFilters.intersection(premiumFilters)   // Only brand:apple
let exclusive = activeFilters.symmetricDifference(premiumFilters)

// Convert Array to Set to remove duplicates
let duplicateIDs = [1,2,2,3,4,4,5]
let uniqueIDs = Set(duplicateIDs)  // {1,2,3,4,5}

  1. Mutability & Performance: let vs var

Swift enforces mutability at the collection level:

  • var = Mutable (can add, remove, change elements).
  • let = Immutable (fixed size and content – like a frozen list).

Copy-on-Write (COW): Assigning a var collection to a new variable doesn't copy the underlying data immediately. It only copies when one of the variables mutates it. This makes Swift collections extremely efficient – even large arrays are cheap to pass around.

SWIFTRead-only
1
// Immutable collection – compiler error if you try to modify
let immutableArray = [1, 2, 3]
// immutableArray.append(4)  // ❌ Compiler error

// Copy-on-Write in action
var original = [1, 2, 3]
var copied = original   // No actual copy yet (both share memory)
copied.append(4)        // ✅ Now a copy is made – original stays [1,2,3]

// Performance tip: Reserve capacity if you know the final size
var largeArray = [Int]()
largeArray.reserveCapacity(1000)  // Avoids repeated reallocation

  1. Functional Transformations (Map, Filter, Reduce)

Swift collections integrate seamlessly with functional programming. These methods are preferred over manual loops for cleaner, more declarative code – especially when transforming data between layers of your architecture (e.g., Repository → UseCase → Presenter).

SWIFTRead-only
1
struct User {
    let id: Int
    let name: String
    let isActive: Bool
}

let users = [
    User(id: 1, name: "Alice", isActive: true),
    User(id: 2, name: "Bob", isActive: false),
    User(id: 3, name: "Charlie", isActive: true)
]

// Map: Transform each element
let activeUserNames = users
    .filter { $0.isActive }           // Keep only active
    .map { $0.name.uppercased() }     // Transform to uppercase names
// Result: ["ALICE", "CHARLIE"]

// Reduce: Combine all elements into one value
let nameString = users.reduce("") { $0 + $1.name + ", " }
// Result: "Alice, Bob, Charlie, "

// CompactMap: Filter out nil results
let strings = ["1", "2", "three", "4"]
let ints = strings.compactMap { Int($0) }  // [1, 2, 4] – "three" ignored

// Grouping dictionaries by a key path
let usersByActiveStatus = Dictionary(grouping: users, by: { $0.isActive })
// Result: [true: [Alice, Charlie], false: [Bob]]

  1. When to Use Each Collection – Architecture Decision Guide

| Use Case | Recommended Collection | Why | |----------|----------------------|-----| | List of dashboard widgets (order matters) | Array | Preserves display order, allows duplicates | | User session store (lookup by userID) | Dictionary | O(1) lookup by ID, no order needed | | Set of permissions for a user | Set | Fast contains() check, ensures uniqueness | | Chat message history | Array | Ordered, indexed access, supports pagination | | Tags on a blog post | Set | No duplicates, fast to check if tag exists | | Configuration flags (key: flag name, value: enabled) | Dictionary | Natural key-value mapping |

Test Your Knowledge

Q1
of 4

You need to store a list of high scores that must remain in the order they were achieved, but duplicates are allowed. Which collection do you use?

A
Set
B
Array
C
Dictionary
D
Tuple
Q2
of 4

What happens if you try to add a duplicate value to a Set?

A
The Set throws a runtime error
B
The duplicate replaces the existing value
C
The duplicate is silently ignored
D
The Set becomes an Array
Q3
of 4

Given `let config: [String: Int] = ["retries": 3]`, what happens when you execute `config["timeout"] = 5`?

A
The value 5 is added with key "timeout"
B
A compiler error because config is immutable
C
It returns nil but does not crash
D
It overwrites the "retries" key
Q4
of 4

Which functional method would you use to transform each element of an array into a new array of the same length?

A
reduce
B
filter
C
compactMap
D
map

Frequently Asked Questions

Why does dictionary access return an Optional?

Because the key you request might not exist. Swift forces you to handle this safely instead of crashing. Use dict[key, default: value] if you want a non-optional fallback, or optional binding (if let).

What does it take for a custom type to be used in a Set or as a Dictionary key?

It must conform to the Hashable protocol. Swift provides automatic conformance if all your stored properties are Hashable. Just declare struct MyKey: Hashable { } – Swift synthesizes it for you. If you need custom logic, implement func hash(into hasher: inout Hasher).

How do I safely get the first element of an Array without crashing?

Use the first property which returns an Optional: if let firstItem = myArray.first { ... }. Avoid subscript access (myArray[0]) unless you are 100% sure the array is not empty.

What's faster: Array.contains() or Set.contains()?

Set.contains() is dramatically faster – O(1) vs Array's O(n). If you frequently check membership and don't need order, convert your Array to a Set first (or store as Set from the beginning).

Previous

swift protocols

Next

swift closures

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.