Swift Cheatsheet
Errors & Concurrency
Use this Swift reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Defining and throwing errors
enum ParseError: Error { case empty case badNumber(String) } func parse(_ text: String) throws -> Int { guard !text.isEmpty else { throw ParseError.empty } guard let value = Int(text) else { throw ParseError.badNumber(text) } return value }
Typed throws (Swift 6) constrain the error type in the signature:
func parseStrict(_ text: String) throws(ParseError) -> Int { guard let v = Int(text) else { throw .badNumber(text) } return v }
do / catch and the try variants
do { let value = try parse("42a") print(value) } catch ParseError.empty { print("nothing to parse") } catch ParseError.badNumber(let text) { print("not a number: \(text)") } catch { print("unexpected: \(error)") // implicit `error` binding } let maybe = try? parse("42") // Int?, nil on any error let forced = try! parse("42") // crashes on error, known-good input only
| Form | Result |
|---|---|
try | Propagates, requires do/catch or a throws function |
try? | Converts errors to nil |
try! | Crashes on error |
defer
func process(_ path: String) throws { let handle = try openFile(path) defer { handle.close() } // runs on every exit: return, throw, fall-through try handle.readAll() }
Multiple defer blocks run in reverse order of declaration. Use them for cleanup that must happen no matter how the scope exits.
Result
func parseResult(_ text: String) -> Result<Int, ParseError> { guard let v = Int(text) else { return .failure(.badNumber(text)) } return .success(v) } switch parseResult("7") { case .success(let v): print(v) case .failure(let err): print(err) } let value = try? parseResult("7").get() // bridge back into throws / optionals let doubled = parseResult("7").map { $0 * 2 }
Result makes an outcome a storable, passable value. Useful for collecting outcomes and for callback APIs that predate async/await.
async / await
func fetchScore(id: Int) async throws -> Int { try await Task.sleep(for: .milliseconds(100)) // stand-in for real async work return id * 10 } func show() async { // call site: try + await do { let score = try await fetchScore(id: 7) print(score) } catch { print("failed: \(error)") } } Task { // bridge from synchronous code await show() }
Top-level await works directly in main.swift. await marks every suspension point where other code may run.
async let and TaskGroup
// Fixed number of concurrent child tasks async let a = fetchScore(id: 1) async let b = fetchScore(id: 2) let combined = try await a + b // both were already running in parallel // Dynamic fan-out let scores = try await withThrowingTaskGroup(of: Int.self) { group in for id in 1...20 { group.addTask { try await fetchScore(id: id) } } var results: [Int] = [] for try await score in group { results.append(score) } return results }
Child tasks are structured: they cannot outlive the scope, and cancellation propagates automatically (check with Task.checkCancellation()).
Actors and @MainActor
actor ScoreBoard {
private var scores: [String: Int] = [:]
func add(_ n: Int, for player: String) {
scores[player, default: 0] += n
}
func total() -> Int { scores.values.reduce(0, +) }
}
let board = ScoreBoard()
await board.add(10, for: "ada") // cross-actor calls are awaited
@MainActor
final class ViewModel { // every member runs on the main thread
var items: [String] = []
func refresh() async {
items = await loadItems() // safe to touch UI-facing state here
}
}Actors serialize access to their mutable state, eliminating data races on it. UI state belongs on @MainActor.
Sendable and Swift 6 strict concurrency
struct Payload: Sendable { // safe to send across concurrency domains let id: Int let tags: [String] }
Value types whose members are all Sendable conform implicitly. Classes qualify only if final with immutable state, or @unchecked Sendable with hand-written locking. The Swift 6 language mode turns data-race violations (non-Sendable values crossing Task/actor boundaries, shared mutable globals) into compile errors. Opt in from Swift 5 mode with -strict-concurrency=complete.