Swift Cheatsheet
Generics & Extensions
Use this Swift reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Generic functions
func swapValues<T>(_ a: inout T, _ b: inout T) { (a, b) = (b, a) } func firstOrDefault<T>(_ xs: [T], _ fallback: T) -> T { xs.first ?? fallback }
Type parameters are inferred at the call site: firstOrDefault([1, 2], 0).
Constraints and where clauses
func largest<T: Comparable>(_ xs: [T]) -> T? { xs.max() } func uniqueSorted<T: Comparable & Hashable>(_ xs: [T]) -> [T] { Set(xs).sorted() } func allEqual<C: Collection>(_ c: C) -> Bool where C.Element: Equatable { guard let first = c.first else { return true } return c.allSatisfy { $0 == first } } extension Collection where Element: Numeric { func total() -> Element { reduce(0, +) } } [1, 2, 3].total() // 6 [1.5, 2.5].total() // 4.0
Generic types and conditional conformance
struct Stack<Element> { private var items: [Element] = [] var isEmpty: Bool { items.isEmpty } var top: Element? { items.last } mutating func push(_ x: Element) { items.append(x) } mutating func pop() -> Element? { items.popLast() } } var stack = Stack<Int>() stack.push(1) // Conformance that exists only when Element qualifies extension Stack: Equatable where Element: Equatable { static func == (l: Stack, r: Stack) -> Bool { l.items == r.items } }
Associated types
protocol Container<Item> { // <Item> = primary associated type (Swift 5.7+) associatedtype Item var count: Int { get } mutating func append(_ item: Item) } struct IntBag: Container { var items: [Int] = [] var count: Int { items.count } mutating func append(_ item: Int) { items.append(item) } // Item inferred as Int }
Primary associated types let generic and existential positions be parameterized:
func consume(_ c: some Container<Int>) { } let strings: any Collection<String> = ["a", "b"]
some vs any in generic code
// some Sequence<Int>: one concrete type, statically dispatched, zero boxing func sum(_ xs: some Sequence<Int>) -> Int { xs.reduce(0, +) } sum(1...10) sum([5, 6]) // any Sequence<Int>: boxed existential, heterogeneous storage allowed var sources: [any Sequence<Int>] = [1...3, [5, 6]]
some Proto in parameter position is sugar for <T: Proto>. Reach for any only when you genuinely need to mix concrete types in one value.
Extensions
extension Int { var isEven: Bool { isMultiple(of: 2) } func times(_ body: () -> Void) { for _ in 0..<self { body() } } } 3.times { print("hi") } extension String { var isPalindrome: Bool { self == String(reversed()) } } "level".isPalindrome // true extension Array where Element == Int { func runningSum() -> [Int] { var total = 0 return map { x in total += x return total } } } [1, 2, 3].runningSum() // [1, 3, 6]
Extensions add computed properties, methods, initializers, subscripts, nested types, and protocol conformances to any type, including types you do not own. They cannot add stored properties.
Custom sorts with closures
struct Job { let start: Int let end: Int } let ordered = jobs.sorted { a, b in a.end == b.end ? a.start < b.start : a.end < b.end } // Tuple comparison covers most multi-key sorts let ordered2 = jobs.sorted { ($0.end, $0.start) < ($1.end, $1.start) } let byName = people.sorted { $0.name < $1.name }