Irbisa · Cheatsheet

Swift Generics & Advanced

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

Middle Developer · 24 questions

  1. 01

    What does Codable do, and how do you decode JSON whose keys do not match your property names?

    Codable is Encodable & Decodable: conform a type and the compiler generates the code that maps its stored properties to and from JSON (or another format) by name.

    import Foundation struct Article: Codable {  let id: Int  let title: String  let authorName: String…
  2. 02

    What are generics in Swift, and what can a where clause express that angle brackets cannot?

    Generics let you write one function or type that works with any type meeting some constraints, while the compiler still checks every use: no Any, no casts.

    // Generic function: one implementation for every Comparable typefunc clamp<T: Comparable>(_ value: T, to range: ClosedRange<T>) -> T {  min(max(value, range.lowerBound), range.upperBound)}print(clamp(15, to: 0...10))     // 10 (T == Int)print(clamp(0.5, to: 0...1))     // 0.5 (T == Double)…
  3. 03

    What is an associated type, what limits does it put on a protocol, and what do primary associated types change?

    An associated type is a placeholder type inside a protocol that each conforming type fills in, like Element in Collection.

    protocol Store<Item> {                  // Item is the primary associated type  associatedtype Item: Identifiable  func load(id: Item.ID) async throws -> Item  func save(_ item: Item) async throws}…
  4. 04

    Which line in this code does not compile, and what does conditional conformance have to do with it?

    struct Box<Value> {  let value: Value}extension Box: Equatable where Value: Equatable {} struct Token { let raw: String } print(Box(value: 1) == Box(value: 1))print([Box(value: "a")] == [Box(value: "a")])print(Box(value: Token(raw: "a")) == Box(value: Token(raw: "a")))

    The third print.

  5. 05

    What is the difference between some and any in Swift, and how do both relate to generics?

    some Shape is an opaque type: one specific concrete type that the compiler knows and hides from the reader.

    protocol Shape { func area() -> Double }struct Circle: Shape { let r: Double; func area() -> Double { .pi * r * r } }struct Square: Shape { let side: Double; func area() -> Double { side * side } } // some in a return: one hidden concrete type, chosen by the functionfunc unitShape() -> some Shape { Circle(r: 1) }…
  6. 06

    This function does not compile. Why, and how would you fix it?

    protocol Shape { func area() -> Double }struct Circle: Shape { let r: Double; func area() -> Double { .pi * r * r } }struct Square: Shape { let side: Double; func area() -> Double { side * side } } func makeShape(rounded: Bool) -> some Shape {  if rounded {    return Circle(r: 1)  }  return Square(side: 2)}

    some Shape promises one concrete type for the whole function, but the two returns produce Circle and Square.

  7. 07

    What does @escaping mean, and why are closure parameters non-escaping by default?

    @escaping says the closure may be called after the function returns, because it is stored or handed to asynchronous work.

    import Foundation final class ImageLoader {  private var handlers: [(Data) -> Void] = []   // Called before the function returns: non-escaping, no annotation needed…
  8. 08

    What are key paths in Swift, and where would you use them?

    A key path is a compile-time-checked reference to a property, written \User.name, that you can store and pass around and later use to read, or write, that property on any instance.

    struct User {  var name: String  var age: Int} let users = [User(name: "Ada", age: 36), User(name: "Linus", age: 29)]…
  9. 09

    How does a property wrapper work, and how do you write a custom one?

    A property wrapper is a type marked @propertyWrapper that owns a property's storage: for @Clamped var volume the compiler stores a hidden _volume and routes every get and set through its wrappedValue.

    @propertyWrapperstruct Clamped<Value: Comparable> {  private var value: Value  private let range: ClosedRange<Value>   init(wrappedValue: Value, _ range: ClosedRange<Value>) {…
  10. 10

    Find the bug: finished ends up lower than the number of completed downloads, although every access to it goes through a lock.

    @propertyWrapperfinal class Locked<Value>: @unchecked Sendable {  private let lock = NSLock()  private var value: Value  init(wrappedValue: Value) { value = wrappedValue }   var wrappedValue: Value {    get { lock.withLock { value } }    set { lock.withLock { value = newValue } }  }} final class DownloadStats: @unchecked Sendable {  @Locked var finished = 0  func markFinished() { finished += 1 }  // called from many download tasks}

    finished += 1 is not one locked operation: it calls the getter (lock, read, unlock), then the setter (lock, write, unlock).

  11. 11

    What is a result builder, and why does an if or a for inside a custom builder sometimes fail to compile?

    A result builder is a type marked @resultBuilder whose static build… methods the compiler calls to turn the statements of a closure into one value.

    struct Step { let title: String } @resultBuilderenum StepsBuilder {  static func buildExpression(_ step: Step) -> [Step] { [step] }        // each line  static func buildBlock(_ parts: [Step]...) -> [Step] { parts.flatMap { $0 } }…
  12. 12

    What is the difference between Sequence and Collection, and how do you make your own type work in a for-in loop?

    A Sequence only promises that you can step through its elements with an iterator, possibly only once.

    // Sequence: an iterator with a mutating next() is all for-in needsstruct Countdown: Sequence {  let start: Int   func makeIterator() -> Iterator { Iterator(current: start) }…
  13. 13

    What is the difference between #if, #available and @available in Swift?

    #if is a compile-time condition: code in a false branch is not compiled into the binary at all.

    import Foundation#if canImport(UIKit)import UIKit                          // only where the SDK has UIKit#endif #if DEBUG…
  14. 14

    How do you use Objective-C code from Swift and Swift code from Objective-C in one target, and what cannot cross the boundary?

    Objective-C is exposed to Swift through a bridging header (an umbrella header in a framework).

    import Foundation // Visible to Objective-C through the generated "App-Swift.h"@objc class Logger: NSObject {  @objc func log(_ message: String) { print(message) }}…
  15. 15

    What does this code print, and why does map run more often than you might expect?

    let numbers = [1, 2, 3, 4, 5, 6]let squares = numbers.lazy  .map { n -> Int in    print("map \(n)")    return n * n  }  .filter { $0 % 2 == 0 } print("start")print(squares.first!)print(squares.first!)

    It prints start, then map 1, map 2, map 2, 4, and the same four lines again for the second first.

  16. 16

    What is type erasure, and when do you still need a hand-written type eraser now that any P<T> exists?

    Type erasure hides a value's concrete type behind a wrapper with a fixed type, so different types that conform to the same protocol can be stored and passed as one type.

    protocol Repository<Item> {  associatedtype Item: Identifiable  func fetch(id: Item.ID) async throws -> Item} struct Order: Identifiable { let id: Int }…
  17. 17

    How does Swift run generic code, and why can an any parameter be slower than a generic one?

    The optimiser can specialise a generic function, emitting a copy for each concrete type it sees, so calls are direct and can be inlined.

  18. 18

    What is a phantom type, and what kind of bug does it prevent?

    A phantom type is a generic parameter that the type never stores; it exists only so the compiler can tell otherwise identical values apart.

    struct ID<Tag>: Hashable, Codable, Sendable {  let rawValue: String} enum UserTag {}      // never instantiated: exists only as a typeenum OrderTag {}…
  19. 19

    What are typed throws (throws(MyError)), and when should you use them instead of plain throws?

    Since Swift 6 a function can declare the exact error type it throws, throws(ParseError), so catch receives a ParseError instead of any Error and can switch over it exhaustively.

    enum ParseError: Error {  case empty  case notANumber(String)  case outOfRange(Int)}…
  20. 20

    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?

    The underlying buffer is shared between copies until someone writes; before the write, a uniqueness check decides whether the buffer has to be cloned first.

    final class Storage {                      // final and private to the struct  var pixels: [UInt8]  init(pixels: [UInt8]) { self.pixels = pixels }  func copy() -> Storage { Storage(pixels: pixels) }}…
  21. 21

    Find the bug: clearing a copy of this Playlist also empties the original.

    struct Playlist {  private final class Storage {    var ids: [Int]    init(ids: [Int]) { self.ids = ids }  }  private var storage = Storage(ids: [])  var ids: [Int] { storage.ids }   mutating func removeAll() { storage.ids.removeAll() }   mutating func append(_ id: Int) {    if !isKnownUniquelyReferenced(&storage) { storage = Storage(ids: storage.ids) }    storage.ids.append(id)  }   mutating func insert(_ id: Int, at index: Int) {    if !isKnownUniquelyReferenced(&storage) { storage = Storage(ids: storage.ids) }    storage.ids.insert(id, at: index)  }}

    removeAll() writes into the shared Storage without the uniqueness check.

  22. 22

    How does Swift implement weak and unowned under the hood (side tables, three reference counts), and why can an object's memory outlive its deinit?

    Every Swift object keeps a strong and an unowned count inline in its header, and the first weak reference moves the counts into a side table that weak references point to.

    final class Session {  let name: String  init(name: String) { self.name = name }  deinit { print("deinit \(name)") }}…
  23. 23

    You need a wrapper for a raw file descriptor that cannot be copied, closed twice or used after closing. What does ~Copyable give you?

    Suppressing Copyable removes implicit copies, so the value has exactly one owner at any moment and the compiler, not code review, proves that it is closed once and never used after that.

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

    When do you write a macro instead of a property wrapper, and why is @Observable a macro?

    A property wrapper changes how one stored property is read and written at run time.

    import Foundationimport Observation @Observablefinal class CartModel {  var items: [String] = []…