Skip to content
Skilled.dev

Language preparation

Swift coding interview preparation: value semantics, optionals, and text

Prepare Swift algorithms with optional results, checked arithmetic, collection value semantics, FIFO traversal, and Unicode-aware text choices.

Published · 5 min read

Swift interview algorithms need a clear model of value semantics, optional results, and text indexing. An Array and a String support different access patterns. A Dictionary lookup returns an optional even when a stored zero is valid. Copying a collection value preserves its independent behavior without promising that every future mutation costs constant time.

The examples use the Swift standard library, with no package dependency. They are checked with Swift 6.2. Match the course exercise's provided signature and node types, and confirm which language choices the lesson's editor offers on your deployment.

Use collection value semantics accurately

Arrays, dictionaries, and sets are value types. Assigning an array to another variable gives independent collection semantics: changing one array's element should not change the other array's corresponding stored value. Implementations can share backing storage until a mutation requires a copy.

If an array contains class instances, those element values are references. Mutating a referenced object's property can therefore be observed through another array containing that same reference. Copying the outer collection does not clone the object graph.

The Swift collection guide explains the built-in collection model. Include the possibility of copying shared storage when discussing a mutation-heavy algorithm with many saved snapshots; value semantics are a behavior contract, not a promise that every copy and write is free.

Return an optional pair and check arithmetic

Two Sum can return an optional tuple of indexes. A nil result means no pair exists; a tuple whose first index is zero is an ordinary success. The dictionary here stores nonoptional indexes, so optional binding distinguishes presence directly.

func twoSum(_ values: [Int], target: Int) -> (Int, Int)? {
    var previous: [Int: Int] = [:]
    for (index, value) in values.enumerated() {
        let (complement, overflow) = target.subtractingReportingOverflow(value)
        if !overflow, let earlier = previous[complement] {
            return (earlier, index)
        }
        previous[value] = index
    }
    return nil
}

func bfsOrder(_ graph: [Int: [Int]], start: Int) -> [Int] {
    var queue = [start]
    var head = 0
    var seen: Set<Int> = [start]
    var order: [Int] = []
    while head < queue.count {
        let node = queue[head]
        head += 1
        order.append(node)
        for neighbor in graph[node, default: []] {
            if seen.insert(neighbor).inserted {
                queue.append(neighbor)
            }
        }
    }
    return order
}

A mathematical complement outside Int's range cannot equal any earlier Int input, so that lookup can be skipped safely. The current value is still stored, because a later value may form a valid pair with it. Checking the complement before insertion also prevents one position from matching itself.

The reporting-overflow operation avoids relying on a trapping subtraction or using wrapping arithmetic as though it preserved the mathematical target. Test a case where the first lookup overflows but a later ordinary pair succeeds. The reference test should use checked addition too, so extreme inputs cannot invalidate the test itself.

Define dictionary presence and equality

A lookup in [Key: Value] returns an optional because a key can be missing. If Value is itself optional, the possible states are more complicated: absent key, present nil payload, and present non-nil payload. Assigning nil through ordinary dictionary subscripting can remove an entry, so choose an explicit representation when those states must remain distinct.

Set membership and dictionary keys require Hashable values. Their equality should match the problem's vertex or coordinate identity. An immutable value-type coordinate can work naturally. A class used as a key needs an intentional equality and hashing policy, especially if its properties can change.

For numeric counts, the default-value dictionary subscript can express a missing count as zero. For a cache or search result, default zero may conceal a meaningful absent case. State what one entry means before choosing that convenience operation.

Use a head index for a short-lived BFS queue

The queue keeps a moving integer head rather than repeatedly calling removeFirst on an Array. Appending adds new work and incrementing head consumes the oldest entry. The Set insertion reports whether a neighbor was newly discovered, so cycles and shared neighbors do not enqueue duplicates.

This representation retains the consumed prefix until the function returns. That is O(V) queue storage across the reachable graph. For a long-lived queue, use a compaction policy, a circular buffer, or a suitable deque implementation so old references do not remain indefinitely. A one-shot traversal does not require all of that extra state.

A missing adjacency key means no outgoing edges. BFS visits reachable vertices in hop layers when edges have equal cost. With bounded integer keys and expected hash operations, traversal work is O(V + E), and visited state plus queue and output use O(V) storage. Priority-based shortest paths need a different frontier order and proof.

Choose a text unit before indexing

Swift String exposes Character values based on extended grapheme clusters. A displayed character may contain several Unicode scalar values or encoded code units. Integer Array indexes therefore do not transfer directly to String, whose index type reflects its variable-length text representation.

Converting a String to [Character] can make integer-position algorithms clearer, at the cost of allocating a collection. Repeatedly finding a String index from its start can also add traversal work inside an outer loop. Count that work rather than assuming every string offset is an O(1) array access.

The strings and characters guide describes these text views. Unicode scalar, UTF-8 byte, and UTF-16 code-unit views support different contracts. An ASCII-only exercise can use a simpler model, while arbitrary user text needs a deliberate segmentation and equality policy.

Make mutation and recursion visible

An inout parameter permits mutation of the caller's value through the function call. Use it when the exercise allows in-place changes, and document what the caller receives afterward. Copying a grid for a nonmutating solution changes the memory analysis even if copy-on-write postpones some allocation.

A recursive tree function still uses call-stack space. Practice an explicit stack for deep shapes, and define how empty children are represented by the supplied optional node type. Force-unwrapping a child does not establish that it exists; handle the empty case where the recursive contract requires it.

For backtracking, restore the working path after recursion and save completed collection values with the intended independence. If those values contain mutable class instances, decide whether sharing them is allowed rather than assuming the outer value copy duplicates every node.

Test value boundaries and shared elements

Check Two Sum on empty input, repeated values, index zero, missing answers, negative values, and Int minimum/maximum cases. Validate distinct in-bounds indexes and a nonoverflowing target sum. For BFS, test cycles, shared neighbors, an isolated start, and a neighbor-only vertex.

Copy a small array, mutate one copy, and verify the other keeps its value. Repeat with an array of class instances to observe the different shared-object behavior. Test text containing an emoji and a combining sequence before claiming what a length or index represents. Then practice Two Sum, BFS, and tree depth with the editor's actual Swift interfaces.

Worked lesson walkthroughs

58 reading lessons with code examples in this language.

Concepts

Concept lesson

Arrays in Swift

Learn array insertion in Swift. Trace a copied result, explain shifting costs, and check empty inputs and endpoint positions without changing the original.

Concept lesson

Big O time and space complexity in Swift

Analyze time and space complexity in Swift with a suffix-copy example. Count nested work, returned storage, and allocation instead of counting loops alone.

Concept lesson

Hash Tables in Swift

Count frequencies with a hash map in Swift. Preserve input order, distinguish missing keys from zero, and explain expected lookup time and retained storage.

Concept lesson

Strings in Swift

Practice string construction in Swift with a repeat-text example. Explain builders, copies, Unicode representation, output size, and boundary cases.

Concept lesson

Palindromes, permutations, and anagrams in Swift

Check an ASCII-normalized palindrome in Swift. Trace two-pointer comparisons, case handling, ignored characters, and the limits of the text contract.

Concept lesson

Recursion in Swift

Trace recursive factorial in Swift from the base case through returning calls. Explain termination, stack space, integer limits, and boundary tests.

Concept lesson

Logarithmic time in Swift: repeated halving and search

Understand logarithmic loops in Swift by counting integer halvings. Test powers of two, explain rounding, and derive the exact step count.

Concept lesson

Binary search in Swift: invariants and boundary tests

Write first-occurrence binary search in Swift. Trace half-open bounds, duplicate values, missing targets, and the random-access assumption behind O(log n).

Concept lesson

Sorting in Swift

Return a numerically sorted copy in Swift. Preserve duplicates and the original input, choose the right library operation, and account for copied storage.

Concept lesson

Linked Lists in Swift

Find a linked list's middle in Swift with slow and fast runners. Define the even-length result, handle an empty list, and separate construction from traversal.

Concept lesson

Pointers in Swift

Trace shared mutation and reference rebinding in Swift. Follow two aliases through one object update and a new allocation, with explicit ownership notes.

Concept lesson

Queues in Swift

Practice FIFO queue behavior in Swift. Compare efficient front removal with a head cursor, preserve repeated values, and count fill-and-drain costs.

Concept lesson

Stacks in Swift

Practice LIFO stack behavior in Swift. Trace push and pop, separate empty state from zero payloads, and explain node ownership and end-operation costs.

Concept lesson

Trees in Swift

Compute binary-tree edge height in Swift. Read heap-indexed nullable slots, handle an empty root, and account for reachable nodes and recursion depth.

Concept lesson

Tree traversal in Swift: in-order, pre-order, and post-order

Trace in-order binary-tree traversal in Swift. Explain nullable child positions, visit order, recursion space, and when the output is actually sorted.

Concept lesson

Binary Search Trees in Swift

Search a binary search tree in Swift. Follow one ordered path, handle absent children, and relate search cost to height without assuming balance.

Concept lesson

Graphs in Swift

Read graph adjacency lists in Swift and compute out-degrees. Define vertex IDs, parallel edges, self-loops, representation costs, and input assumptions.

Concept lesson

Depth-First Search in Swift

Implement iterative DFS in Swift. Preserve listed neighbor order, skip repeated visits, trace a shared descendant, and explain pending-stack costs.

Concept lesson

Breadth-First Search in Swift

Implement BFS in Swift with FIFO order and enqueue-time discovery. Trace graph layers, cycles, shared descendants, and visited-storage costs.

Concept lesson

Subproblems and Overlapping Subproblems in Swift

Count naive Fibonacci calls in Swift. Trace overlapping states, include base-case work, and identify the cache key before applying memoization.

Concept lesson

Memoization in Swift

Memoize Fibonacci in Swift and count distinct computed states. Preserve cached zero, define cache lifetime, and explain remaining recursion depth.

Concept lesson

Bottom-Up vs. Top-Down in Swift

Build a bottom-up Fibonacci table in Swift. Follow dependency order, test zero input, and compare full-table storage with rolling-state alternatives.

Concept lesson

Dynamic Programming in Swift

Solve minimum coin count in Swift. Trace the recurrence, represent unreachable states, test a greedy counterexample, and explain amount-based table costs.

Data structure implementations

Implementation tutorial

Implement a dynamic array in Swift

Implement a map-backed teaching array in Swift. Trace push, pop, insertion, and removal, including logical length, missing indices, and obsolete entries.

Implementation tutorial

Implement a hash table in Swift: collisions, updates, and removal

Build a hash table in Swift with separate chaining. Follow colliding keys through lookup, update, and deletion, and explain the limits of a fixed teaching hash.

Implementation tutorial

Implement a linked list in Swift: head, tail, and node identity

Build a doubly linked list in Swift. Trace prepend and append, verify reciprocal links and both endpoints, and explain ownership and retention.

Implementation tutorial

Implement a queue in Swift: FIFO links and empty transitions

Implement a linked queue in Swift. Trace FIFO order, the last removal, and refilling an empty queue while preserving first and last endpoint invariants.

Implementation tutorial

Implement a stack in Swift: push, pop, and ownership

Implement a linked stack in Swift. Follow the top link through push, peek, and pop, including empty state, returned nodes, and retained chains.

Implementation tutorial

Implement a binary search tree in Swift: insert and search

Build a binary search tree in Swift. Trace insertion and search, preserve duplicate keys on the right, and test skewed shapes without assuming balancing.

Implementation tutorial

Implement a graph in Swift: adjacency lists and edge removal

Build an adjacency-list graph in Swift. Trace directed and undirected edges, self-loops, parallel entries, and removal of incoming and outgoing edges.

Practice problems

Practice walkthrough

Two Sum in Swift: all index pairs with Dictionary and overflow checks

Return all matching index pairs in Swift with Dictionary optional binding. Detect complement overflow and keep the original array unchanged.

Practice walkthrough

Valid brackets in Swift: Unicode scalars and a safe array stack

Validate bracket nesting with Swift Unicode-scalar iteration and popLast. Ignore ordinary text, recognize brackets beside combining marks, and test empty-stack handling.

Practice walkthrough

Product of array except self in Swift: two passes with value-semantic arrays

Compute product of array except self in Swift with two passes. Explain reversed indices, value semantics, zeros, and trapping integer overflow.

Practice walkthrough

Counting sort for ages in Swift: worked solution

Sort bounded ages with a frequency table, preserving duplicate values. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Best stock profit in Swift: one transaction

Find the best single chronological stock trade with one scan. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Most frequent email in Swift: hash-map solution

Count email occurrences and return the unique most frequent address. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

First Bad Version in Swift: boundary binary search

Find the first true value in a monotone version predicate. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Reverse a linked list in Swift: pointer walkthrough

Reverse a linked list by moving nodes from an untouched suffix to a reversed prefix. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Remove kth node from end in Swift: zero-based deletion

Delete the zero-based kth node from the end of a linked list. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge sorted linked lists in Swift: Combine Blockchains

Merge two sorted linked chains while preserving duplicate timestamps. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count Islands in Swift: DFS flood-fill walkthrough

Count side-connected land components with a destructive flood fill of the working grid. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate a binary search tree in Swift: inherited bounds

Validate every binary-tree node against bounds inherited from all its ancestors. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Binary tree maximum depth in Swift: count edges

Measure a binary tree’s longest root-to-leaf path in edges. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Lowest common ancestor in Swift: return the original node

Find the lowest common ancestor in a general binary tree and return the original node. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Generate string permutations in Swift: positional results

Generate positional string permutations, retaining repeated results for repeated characters. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Group palindrome permutations in Swift: counts and keys

Filter palindrome permutations, then group retained strings by character multiset. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge intervals in Swift: Interview Scheduler

Merge sorted interval coverage with an explicit touching-endpoint rule. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Min-max stack in Swift: constant-time extrema

Support stack extrema through histories that retain duplicate record values. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

BFS shortest path in Swift: Pilot a Rocket

Find an unweighted shortest route and reconstruct it from predecessor links. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Maze reachability in Swift: memoized down/right search

Memoize directed maze reachability while keeping unknown distinct from false. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a rotated sorted array in Swift: Shifted Search

Search a distinct rotated sorted array by identifying an ordered half. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a sorted matrix in Swift: staircase walkthrough

Eliminate a row or column at each step in a row-and-column sorted matrix. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

House Robber in Swift: maximum nonadjacent sum

Maximize a nonadjacent selection with two rolling prefix optima. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Sum decimal digits in Swift: recursive solution

Sum decimal digits through a decreasing recursive integer decomposition. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count 1, 2, or 3-step paths in Swift: Three Moves

Count ordered one-, two-, and three-step routes with rolling dynamic programming. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Coin change in Swift: minimum-coin dynamic programming

Minimize unlimited coin usage with a bottom-up amount table. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate Sudoku in Swift: rows, columns, and subsquares

Validate filled Sudoku entries against rows, columns, and subsquares. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Spiral matrix traversal in Swift: Walk a Matrix

Traverse a rectangle in clockwise spiral order using shrinking boundaries. Follow the Swift implementation, its correctness argument, worked example, complexity, and boundary tests.