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.
Practice now
Study without an account
let vs var in SwiftScheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1
letvsvarin Swiftletdeclares a constant that can’t be reassigned;vardeclares a variable. Preferlet; the compiler suggests it when avarnever changes - 2Interpolate values into a stringswift
let message = "Hello, \(name)! You have \(items.count) items." - 3What is an optional?A type like
String?that holds either a value ornil. Non-optional types can never benil, so you must unwrap an optional before using its value - 4Unwrap an optional with
if letswiftif let name = user.name { print("Hi, \(name)") } else { print("No name") }Since Swift 5.7 you can writeif let name { … }to unwrap into a constant with the same name. - 5
guard letvsif letguard letunwraps for the rest of the scope, and itselsebranch must exit (return,throw,breakorcontinue), which keeps the happy path unindentedswiftguard let url = URL(string: text) else { return } load(url) - 6Provide a default for an optionalswift
let count = Int(text) ?? 0??is the nil-coalescing operator. - 7What is the type of
user?.address?.cityifcityis aString?String?: optional chaining returnsnilas soon as any link isnil, so the result is always optional - 8What happens when you force-unwrap
nilwith!?The program crashes with “Unexpectedly found nil while unwrapping an Optional value”. Use!only whennilis truly impossible - 9What does this print?swift
let n = Int("42a") print(n ?? -1)-1:Int(_:)is a failable initializer that returnsnilwhen the string isn’t a valid integer - 10What does this print?swift
let x: Int? = 5 print(x)Optional(5), with a compiler warning. Unwrap it first, or writex ?? 0, to print5 - 11Struct vs class in SwiftStructs 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 ARCApple recommends structs by default, and classes when you need identity or shared mutable state. - 12What 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 itIfPointwere a class,aandbwould refer to the same object and it would print5. - 13Why must some struct methods be marked
mutating?A struct method can’t modifyselfunless it is markedmutating, and mutating methods can’t be called on aletconstantswiftstruct Counter { var count = 0 mutating func increment() { count += 1 } } - 14
letwith a struct vsletwith a classAletstruct is fully immutable, so you can’t change its properties. Aletclass reference can’t be reassigned, but the object’svarproperties can still change - 15Why is copying a large
Arraycheap?Standard collections (Array,Dictionary,Set) andStringuse copy-on-write: copies share storage until one of them is mutated - 16What does
finaldo on a class?Prevents subclassing, which also lets the compiler call its methods directly instead of through dynamic dispatch - 17Define and adopt a protocolswift
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. - 18Give a protocol method a default implementationswift
extension Shape { func describe() -> String { "Area: \(area)" } }Every conforming type getsdescribe()for free through the protocol extension. - 19
some Shapevsany Shapesome Shapeis an opaque type: one specific concrete type that the caller doesn’t get to see.any Shapeis an existential box that can hold any conforming type, at some runtime costswiftfunc makeShape() -> some Shape { Square(side: 2) } let shapes: [any Shape] = [Square(side: 1), Circle(radius: 2)] - 20Get equality, hashing and JSON support for a structswift
struct User: Codable, Hashable { let id: Int let name: String }The compiler synthesizes the conformances when every stored property already conforms. - 21Decode JSON into a
Codablestructswiftlet user = try JSONDecoder().decode(User.self, from: data) - 22A generic function constrained to comparable valuesswift
func largest<T: Comparable>(_ items: [T]) -> T? { items.max() } - 23An enum with associated valuesswift
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. - 24Extract associated values with
switchswiftswitch payment { case .cash: print("Cash") case .card(let number): print("Card \(number)") case let .transfer(iban, _): print("Transfer to \(iban)") }Aswitchmust be exhaustive, and cases don’t fall through. - 25What is
Planet.venus.rawValue?swiftenum Planet: Int { case mercury = 1, venus, earth }2: integer raw values count up from the previous casePlanet(rawValue: 3)returns an optional, here.earth; an unknown raw value givesnil. - 26List every case of an enumswift
enum Direction: CaseIterable { case north, south, east, west } Direction.allCases.count // 4 - 27How is
Optionaldefined?As a generic enum with two cases,.noneand.some(Wrapped);nilis shorthand for.noneswiftenum Optional<Wrapped> { case none case some(Wrapped) } - 28Double every number with
mapand shorthand closure syntaxswiftlet doubled = numbers.map { $0 * 2 }The full form isnumbers.map { (n: Int) -> Int in n * 2 }. - 29Sort an array in descending orderswift
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. - 30What does
@escapingmean 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 - 31Why write
[weak self]in a closure?An escaping closure capturesselfstrongly; ifselfalso holds on to the closure, neither is ever freed.[weak self]breaks the cycle and makesselfoptional inside the closureswiftviewModel.onUpdate = { [weak self] in self?.render() } - 32What 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 originalA capture list like{ [count] in … }copies the value when the closure is created instead. - 33Convert strings to numbers, dropping the ones that failswift
let numbers = ["1", "x", "3"].compactMap { Int($0) } // [1, 3] - 34What 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 - 35What 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 leakARC has no cycle collector; break cycles with
weakorunowned. - 36
weakvsunownedweakreferences are optional and becomenilwhen the object is deallocated;unownedreferences are non-optional and assume the object outlives them, so using one after deallocation crashes - 37Declare a delegate property without a retain cycleswift
protocol DownloaderDelegate: AnyObject { func didFinish(_ data: Data) } final class Downloader { weak var delegate: DownloaderDelegate? }The protocol is restricted to classes withAnyObject, because only class instances can be referenced weakly. - 38Call a throwing function and handle the errorswift
do { let data = try loadFile(at: path) print(data.count) } catch { print("Failed: \(error)") }Inside a barecatch, the error is available aserror. - 39
try?vstry!try?turns a thrown error intonil, giving an optional result;try!crashes if an error is thrown - 40Declare and call an async throwing functionswift
func fetchUser(id: Int) async throws -> User { /* … */ } let user = try await fetchUser(id: 42)async/await arrived in Swift 5.5. - 41Start two async calls in parallel and wait for bothswift
async let profile = loadProfile() async let friends = loadFriends() let (p, f) = await (profile, friends)Eachasync letstarts a child task immediately;awaitcollects the results. - 42Call async code from a synchronous context, like a button actionswift
Button("Refresh") { Task { await viewModel.refresh() } }Task { … }starts a new unstructured task that inherits the current actor. - 43What 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 - 44What does
@MainActordo?Makes a function, property or whole type run on the main actor, i.e. the main thread, where UI updates must happen - 45The shape of a SwiftUI viewswift
struct Greeting: View { let name: String var body: some View { Text("Hello, \(name)") } }Views are lightweight structs; SwiftUI recomputesbodywhenever the data it depends on changes. - 46What is
@State?A property wrapper for simple state a view owns; changing it makes SwiftUI recompute the view’sbodyswiftstruct Counter: View { @State private var count = 0 var body: some View { Button("Tapped \(count) times") { count += 1 } } } - 47Let a child view edit its parent’s
@StateDeclare a@Bindingin the child and pass the state with a$prefixswiftstruct 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) } } - 48Share 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@Observablemacro, views update when the properties they read change. Older code usesObservableObjectwith@Publishedand@StateObject. - 49Do 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 - 50SwiftUI’s basic layout containers
VStackstacks views vertically,HStackhorizontally andZStackfront to back;Spacer()takes up free space, andListbuilds 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.
More in Programming
JavaScript
Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.
50 cardsAdvanced 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 cardsTypeScript
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 cardsPython
Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.
50 cardsGit & terminal
The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.
50 cardsJava
Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.
50 cardsC#
C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.
50 cardsKotlin
Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.
50 cardsFlutter
Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.
50 cardsPHP
Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.
50 cardsRuby
Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.
50 cardsGo
Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.
50 cardsRust
Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.
50 cardsC++
Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.
50 cardsHaskell
Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.
50 cardsElixir
Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.
50 cardsx86-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 cardsRegex
Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.
50 cards