askesis

Swift essentials: 50 cards on optionals, protocols, ARC and SwiftUI

Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.

ProgrammingBeginner50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
let vs var in Swift

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    let vs var in Swift
    let declares a constant that can’t be reassigned; var declares a variable. Prefer let; the compiler suggests it when a var never changes
  2. 2
    Interpolate values into a string
    swift
    let message = "Hello, \(name)! You have \(items.count) items."
  3. 3
    What is an optional?
    A type like String? that holds either a value or nil. Non-optional types can never be nil, so you must unwrap an optional before using its value
  4. 4
    Unwrap an optional with if let
    swift
    if let name = user.name {
        print("Hi, \(name)")
    } else {
        print("No name")
    }
    Since Swift 5.7 you can write if let name { … } to unwrap into a constant with the same name.
  5. 5
    guard let vs if let
    guard let unwraps for the rest of the scope, and its else branch must exit (return, throw, break or continue), which keeps the happy path unindented
    swift
    guard let url = URL(string: text) else { return }
    load(url)
  6. 6
    Provide a default for an optional
    swift
    let count = Int(text) ?? 0
    ?? is the nil-coalescing operator.
  7. 7
    What is the type of user?.address?.city if city is a String?
    String?: optional chaining returns nil as soon as any link is nil, so the result is always optional
  8. 8
    What happens when you force-unwrap nil with !?
    The program crashes with “Unexpectedly found nil while unwrapping an Optional value”. Use ! only when nil is truly impossible
  9. 9
    What does this print?
    swift
    let n = Int("42a")
    print(n ?? -1)
    -1: Int(_:) is a failable initializer that returns nil when the string isn’t a valid integer
  10. 10
    What does this print?
    swift
    let x: Int? = 5
    print(x)
    Optional(5), with a compiler warning. Unwrap it first, or write x ?? 0, to print 5
  11. 11
    Struct vs class in Swift
    Structs are value types (copied on assignment) with no inheritance and a free memberwise initializer; classes are reference types with inheritance, identity (===) and deinitializers, managed by ARC
    Apple recommends structs by default, and classes when you need identity or shared mutable state.
  12. 12
    What does this print?
    swift
    struct Point { var x = 0 }
    
    var a = Point()
    var b = a
    b.x = 5
    print(a.x)
    0: assigning a struct copies it
    If Point were a class, a and b would refer to the same object and it would print 5.
  13. 13
    Why must some struct methods be marked mutating?
    A struct method can’t modify self unless it is marked mutating, and mutating methods can’t be called on a let constant
    swift
    struct Counter {
        var count = 0
        mutating func increment() { count += 1 }
    }
  14. 14
    let with a struct vs let with a class
    A let struct is fully immutable, so you can’t change its properties. A let class reference can’t be reassigned, but the object’s var properties can still change
  15. 15
    Why is copying a large Array cheap?
    Standard collections (Array, Dictionary, Set) and String use copy-on-write: copies share storage until one of them is mutated
  16. 16
    What does final do on a class?
    Prevents subclassing, which also lets the compiler call its methods directly instead of through dynamic dispatch
  17. 17
    Define and adopt a protocol
    swift
    protocol Shape {
        var area: Double { get }
    }
    
    struct Square: Shape {
        let side: Double
        var area: Double { side * side }
    }
    Structs, classes and enums can all conform to protocols.
  18. 18
    Give a protocol method a default implementation
    swift
    extension Shape {
        func describe() -> String { "Area: \(area)" }
    }
    Every conforming type gets describe() for free through the protocol extension.
  19. 19
    some Shape vs any Shape
    some Shape is an opaque type: one specific concrete type that the caller doesn’t get to see. any Shape is an existential box that can hold any conforming type, at some runtime cost
    swift
    func makeShape() -> some Shape { Square(side: 2) }
    let shapes: [any Shape] = [Square(side: 1), Circle(radius: 2)]
  20. 20
    Get equality, hashing and JSON support for a struct
    swift
    struct User: Codable, Hashable {
        let id: Int
        let name: String
    }
    The compiler synthesizes the conformances when every stored property already conforms.
  21. 21
    Decode JSON into a Codable struct
    swift
    let user = try JSONDecoder().decode(User.self, from: data)
  22. 22
    A generic function constrained to comparable values
    swift
    func largest<T: Comparable>(_ items: [T]) -> T? {
        items.max()
    }
  23. 23
    An enum with associated values
    swift
    enum Payment {
        case cash
        case card(number: String)
        case transfer(iban: String, reference: String)
    }
    Each case can carry different data, which makes enums a good fit for modeling states.
  24. 24
    Extract associated values with switch
    swift
    switch payment {
    case .cash:
        print("Cash")
    case .card(let number):
        print("Card \(number)")
    case let .transfer(iban, _):
        print("Transfer to \(iban)")
    }
    A switch must be exhaustive, and cases don’t fall through.
  25. 25
    What is Planet.venus.rawValue?
    swift
    enum Planet: Int {
        case mercury = 1, venus, earth
    }
    2: integer raw values count up from the previous case
    Planet(rawValue: 3) returns an optional, here .earth; an unknown raw value gives nil.
  26. 26
    List every case of an enum
    swift
    enum Direction: CaseIterable { case north, south, east, west }
    
    Direction.allCases.count // 4
  27. 27
    How is Optional defined?
    As a generic enum with two cases, .none and .some(Wrapped); nil is shorthand for .none
    swift
    enum Optional<Wrapped> {
        case none
        case some(Wrapped)
    }
  28. 28
    Double every number with map and shorthand closure syntax
    swift
    let doubled = numbers.map { $0 * 2 }
    The full form is numbers.map { (n: Int) -> Int in n * 2 }.
  29. 29
    Sort an array in descending order
    swift
    let sorted = numbers.sorted { $0 > $1 }
    // or: numbers.sorted(by: >)
    A trailing closure goes after the parentheses, which can be dropped when it is the only argument.
  30. 30
    What does @escaping mean on a closure parameter?
    The closure may be called after the function returns, e.g. stored in a property or used as a completion handler. Closure parameters are non-escaping by default
  31. 31
    Why write [weak self] in a closure?
    An escaping closure captures self strongly; if self also holds on to the closure, neither is ever freed. [weak self] breaks the cycle and makes self optional inside the closure
    swift
    viewModel.onUpdate = { [weak self] in
        self?.render()
    }
  32. 32
    What does this print?
    swift
    var count = 0
    let increment = { count += 1 }
    increment()
    increment()
    print(count)
    2: closures capture variables by reference, so they see and modify the original
    A capture list like { [count] in … } copies the value when the closure is created instead.
  33. 33
    Convert strings to numbers, dropping the ones that fail
    swift
    let numbers = ["1", "x", "3"].compactMap { Int($0) } // [1, 3]
  34. 34
    What is ARC?
    Automatic Reference Counting: the compiler inserts retain and release calls so each class instance tracks its strong references, and it is deallocated (running deinit) as soon as the count reaches zero
  35. 35
    What is a retain cycle?
    Two class instances, or an instance and a closure, holding strong references to each other: neither count reaches zero, so both leak
    ARC has no cycle collector; break cycles with weak or unowned.
  36. 36
    weak vs unowned
    weak references are optional and become nil when the object is deallocated; unowned references are non-optional and assume the object outlives them, so using one after deallocation crashes
  37. 37
    Declare a delegate property without a retain cycle
    swift
    protocol DownloaderDelegate: AnyObject {
        func didFinish(_ data: Data)
    }
    
    final class Downloader {
        weak var delegate: DownloaderDelegate?
    }
    The protocol is restricted to classes with AnyObject, because only class instances can be referenced weakly.
  38. 38
    Call a throwing function and handle the error
    swift
    do {
        let data = try loadFile(at: path)
        print(data.count)
    } catch {
        print("Failed: \(error)")
    }
    Inside a bare catch, the error is available as error.
  39. 39
    try? vs try!
    try? turns a thrown error into nil, giving an optional result; try! crashes if an error is thrown
  40. 40
    Declare and call an async throwing function
    swift
    func fetchUser(id: Int) async throws -> User { /* … */ }
    
    let user = try await fetchUser(id: 42)
    async/await arrived in Swift 5.5.
  41. 41
    Start two async calls in parallel and wait for both
    swift
    async let profile = loadProfile()
    async let friends = loadFriends()
    let (p, f) = await (profile, friends)
    Each async let starts a child task immediately; await collects the results.
  42. 42
    Call async code from a synchronous context, like a button action
    swift
    Button("Refresh") {
        Task { await viewModel.refresh() }
    }
    Task { … } starts a new unstructured task that inherits the current actor.
  43. 43
    What is an actor?
    A reference type that protects its mutable state by letting only one task access it at a time; calls from outside are asynchronous (await counter.increment()), which prevents data races
  44. 44
    What does @MainActor do?
    Makes a function, property or whole type run on the main actor, i.e. the main thread, where UI updates must happen
  45. 45
    The shape of a SwiftUI view
    swift
    struct Greeting: View {
        let name: String
    
        var body: some View {
            Text("Hello, \(name)")
        }
    }
    Views are lightweight structs; SwiftUI recomputes body whenever the data it depends on changes.
  46. 46
    What is @State?
    A property wrapper for simple state a view owns; changing it makes SwiftUI recompute the view’s body
    swift
    struct Counter: View {
        @State private var count = 0
    
        var body: some View {
            Button("Tapped \(count) times") { count += 1 }
        }
    }
  47. 47
    Let a child view edit its parent’s @State
    Declare a @Binding in the child and pass the state with a $ prefix
    swift
    struct Settings: View {
        @State private var isOn = false
        var body: some View { EnabledRow(isOn: $isOn) }
    }
    
    struct EnabledRow: View {
        @Binding var isOn: Bool
        var body: some View { Toggle("Enabled", isOn: $isOn) }
    }
  48. 48
    Share an observable model between SwiftUI views (iOS 17+)
    swift
    @Observable
    final class Cart {
        var items: [Item] = []
    }
    
    struct CartView: View {
        @State private var cart = Cart()
        var body: some View { Text("\(cart.items.count) items") }
    }
    With the @Observable macro, views update when the properties they read change. Older code uses ObservableObject with @Published and @StateObject.
  49. 49
    Do these two views look the same?
    swift
    Text("Hi").padding().background(.yellow)
    Text("Hi").background(.yellow).padding()
    No. Modifiers wrap the view in order: the first paints yellow behind the text and its padding; the second paints only behind the text, then adds clear padding around it
  50. 50
    SwiftUI’s basic layout containers
    VStack stacks views vertically, HStack horizontally and ZStack front to back; Spacer() takes up free space, and List builds a scrolling list

Frequently asked questions

Which Swift version does the deck use?

Current Swift 6. Newer features are marked, such as async/await (5.5), the if let shorthand (5.7) and the @Observable macro (5.9, iOS 17).

Is this deck enough to build iOS apps?

It covers the language and the SwiftUI basics you need first. Navigation, networking and frameworks like SwiftData deserve their own study.

Should I use a struct or a class in Swift?

Start with a struct. Use a class when you need shared identity, inheritance or reference semantics, such as a model object that several views observe.

Intermediate

JavaScript

Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.

50 cards
Intermediate

Advanced JS

Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.

50 cards
Intermediate

TypeScript

Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.

50 cards
Beginner

Python

Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.

50 cards
Beginner

Git & terminal

The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.

50 cards
Beginner

Java

Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.

50 cards
Beginner

C#

C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.

50 cards
Beginner

Kotlin

Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.

50 cards
Beginner

Flutter

Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.

50 cards
Beginner

PHP

Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.

50 cards
Beginner

Ruby

Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.

50 cards
Beginner

Go

Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.

50 cards
Intermediate

Rust

Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.

50 cards
Intermediate

C++

Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.

50 cards
Intermediate

Haskell

Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.

50 cards
Intermediate

Elixir

Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.

50 cards
Intermediate

x86-64 assembly

x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.

50 cards
Beginner

Regex

Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.

50 cards