Swift Generics & Advanced
Irbisa · cheatsheetSeptember 13, 2026

Swift Generics & Advanced

Generics, opaque types, property wrappers, result builders, key paths

Middle Developer20 itemscompressed for a skim
  1. 01

    What are generics in Swift? How do where clauses work?

    Medium

    Generics let you write one implementation that works for many types while keeping full type safety — no Any, no casting, no boxing.

    // Generic function with constraint
    func clamp<T: Comparable>(_ value: T, to range: ClosedRange<T>) -> T {
      min(max(value, range.lowerBound), range.upperBound)
    }
    
    // Generic type
    …
  2. 02

    What are opaque types (some) vs existentials (any) in Swift?

    Hard

    Both some and any let you refer to a protocol, but some names one hidden concrete type while any is a box that can hold a different type each time.

    protocol Shape { func area() -> Double }
    struct Circle: Shape { let r: Double; func area() -> Double { .pi * r * r } }
    struct Square: Shape { let s: Double; func area() -> Double { s * s } }
    
    // some — single concrete type, hidden
    func randomShape() -> some Shape { Circle(r: 1) }
    …
  3. 03

    What are property wrappers in Swift?

    Hard

    Property wrappers package storage + behavior into a reusable annotation.

    // Define a wrapper
    @propertyWrapper
    struct Clamped<Value: Comparable> {
      private var value: Value
      let range: ClosedRange<Value>
      init(wrappedValue: Value, _ range: ClosedRange<Value>) {
    …
  4. 04

    What are result builders (DSLs) in Swift?

    Hard

    Result builders (originally function builders) let you write declarative DSLs in Swift.

    // A toy HTML builder DSL
    @resultBuilder
    struct HTMLBuilder {
      static func buildBlock(_ parts: String...) -> String {
        parts.joined()
      }
    …
  5. 05

    What are key paths in Swift?

    Medium

    A key path is a typed, storable reference to a property that you write with a backslash and pass around like any other value.

    struct User { var name: String; var age: Int }
    let users = [User(name: "Alice", age: 30), User(name: "Bob", age: 25)]
    
    // Key path literal
    let kp = \User.name      // KeyPath<User, String>
    print(users[0][keyPath: kp]) // Alice
    …
  6. 06

    Explain Codable in Swift and how to customize JSON encoding/decoding.

    Medium

    Codable is just Encodable & Decodable, and conforming makes the compiler synthesise a symmetric encoder and decoder over your stored properties.

    struct Article: Codable {
      let id: UUID
      let title: String
      let publishedAt: Date
      let tags: [String]
      let author: String
    …
  7. 07

    Your protocol has an associatedtype and you now need to store a mixed collection of conformers. What are the options?

    Hard

    A protocol with an associated type describes a family of types rather than one type, so before you can store conformers you have to either pin that associated type or erase it.

    protocol Repository<Item> {                 // Item is a primary associated type
      associatedtype Item: Identifiable
      func fetch(id: Item.ID) async throws -> Item
      func save(_ item: Item) async throws
    }
    …
  8. 08

    What does @escaping actually change, and why is non-escaping the default?

    Medium

    It tells the compiler the closure may still be alive after the function returns, so the closure and everything it captured have to outlive the call.

    final class ImageLoader {
      private var handlers: [(UIImage) -> Void] = []
    
      // Runs before the function returns — nothing to annotate
      func withPlaceholder(_ body: (UIImage) -> Void) {
        body(.placeholder)
    …
  9. 09

    What are Swift macros, what kinds exist, and what do they cost?

    Medium

    A macro is a compiler plugin that generates source code during the build, so what you ship is ordinary Swift you can read, step through and debug.

    // ── Declaration (your library target) ──────────────────────────
    @attached(member, names: named(init), named(memberwiseValues))
    @attached(extension, conformances: Codable)
    public macro Model() = #externalMacro(module: "AppMacros", type: "ModelMacro")
    
    @freestanding(expression)
    …
  10. 10

    Passing a big Array around in Swift looks free. How does copy-on-write work, and how would you implement it for your own type?

    Hard

    The underlying buffer is shared between copies until somebody writes, and a uniqueness check decides whether that write has to clone first.

    final class Storage {                     // private, final, single owner
      var pixels: [UInt8]
      init(pixels: [UInt8]) { self.pixels = pixels }
      func copy() -> Storage { Storage(pixels: pixels) }
    }
    …
  11. 11

    How do you make your own type work in a for-in loop, and what does adding lazy change?

    Medium

    Conforming to Sequence with an iterator is what the loop actually requires, and it hands you the whole standard library of algorithms at the same time.

    struct Countdown: Sequence {
      let start: Int
    
      func makeIterator() -> Iterator { Iterator(current: start) }
    
      struct Iterator: IteratorProtocol {
    …
  12. 12

    How does SwiftUI let you write .buttonStyle(.bordered) instead of naming the style's type?

    Medium

    A protocol extension constrained to one concrete conforming type can hold a static property, and leading-dot syntax finds it from the type the compiler expects at that position.

    protocol Validator {
      associatedtype Value
      func validate(_ value: Value) throws
    }
    
    struct NotEmpty: Validator {
    …
  13. 13

    Xcode says it cannot type-check a fifteen-line expression in reasonable time and nothing in it looks wrong — what is the compiler doing?

    Medium

    It is solving one constraint system over every overload of every operator and every literal in that expression, and the search space multiplies with each term until a timer stops it.

    import SwiftUI
    
    // ❌ One expression: the literals are still open (Int? Double? CGFloat?), + is
    // overloaded dozens of times, and each ternary makes the solver try both
    // branches against every candidate that is still standing.
    func slowPadding(isCompact: Bool, hasIcon: Bool, iconWidth: CGFloat, inset: CGFloat) -> CGFloat {
    …
  14. 14

    Your property wrapper's setter compiles in a class but not in a struct, yet SwiftUI writes to @State from an immutable view — how does that work?

    Medium

    A wrappedValue setter that changes storage held inside the wrapper is mutating, and mutating the wrapper means mutating the value that contains it — unless the wrapper writes through a reference and declares nonmutating set.

    // Storage lives inside the wrapper, so the setter must be mutating.
    @propertyWrapper
    struct Clamped<Value: Comparable> {
      private var value: Value
      private let range: ClosedRange<Value>
    …
  15. 15

    A colleague adds an if let and a for loop to your custom @resultBuilder DSL and it stops compiling — what is missing?

    Medium

    A result builder supports exactly the control flow whose build methods you implemented: if without else needs buildOptional, if/else and switch need buildEither, and `for ...

    struct Step { let title: String }
    
    @resultBuilder
    enum StepBuilder {
      static func buildBlock(_ parts: [Step]...) -> [Step] { parts.flatMap { $0 } }
    …
  16. 16

    How can Logger accept an interpolation with extra arguments, as in "user (id, privacy: .private)", when String interpolation takes none?

    Medium

    Because the argument is not a String at all — it is a type conforming to ExpressibleByStringInterpolation whose interpolation type declares its own appendInterpolation overloads, and each \(...) is resolved as a normal call to one of them.

    import Foundation
    
    // A query type where interpolated values become bound parameters, never SQL text.
    struct SQLQuery: ExpressibleByStringInterpolation {
      let sql: String
      let bindings: [any Encodable]
    …
  17. 17

    A generic helper was fast inside the app target and visibly slower after you moved it into a package — what changed?

    Hard

    Inside one module the optimiser specialises a generic function for every concrete type it sees called with; across a module boundary the body is not available to the client, so it links against the unspecialised version.

    // ---- Module: Analytics (a separate package target) ----------------------
    
    public struct Bucketer<Key: Hashable> {
      // An inlinable body may only reference public or @usableFromInline symbols.
      @usableFromInline let capacity: Int
    …
  18. 18

    You need a wrapper around a raw file descriptor that cannot be copied, double-closed or used after closing — what does ~Copyable give you?

    Hard

    Suppressing Copyable removes the implicit copy, so the value has exactly one owner at any moment and the compiler — not code review — proves that close happens once.

    import Darwin
    
    struct FileDescriptor: ~Copyable {
      private let fd: Int32
    
      init?(opening path: String) {
    …
  19. 19

    Your reactive layer has six copy-pasted combineLatest overloads for two through seven inputs — how do parameter packs collapse them into one?

    Hard

    Declare the generic parameter as a pack with each, then expand it with repeat wherever a list of types or values belongs, and one declaration covers every arity.

    import Foundation
    
    struct Store<Value> {
      private(set) var value: Value
    }
    …
  20. 20

    A settings screen wants rows of (title, key path), but an array of WritableKeyPaths with different value types will not compile — what do you store instead?

    Hard

    Erasing a key path erases its Value type as well, and the erased forms are read-only — so you keep the typed key path but capture it in a closure, or keep the value type in an enum case.

    struct Settings {
      var isDarkMode = false
      var fontSize = 14
      var username = ""
    }
    …