iOS cheat sheet

A scannable iOS reference: 30 short snippets across 13 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
State and navigationSwiftUI is a function of state: change the state, and the framework recomputes the affected views. Picking the wronglesson
Persistence and the App StoreReads and writes to a @Model are main-actor friendly for small apps, but heavy imports and background sync belong on alesson
Swift essentials for iOSSwiftUI views are structs precisely because value semantics make diffing cheap and predictable. Protocols add thelesson
Layout, stacks and adaptive UIGeometryReader is greedy: it expands to the space offered and reports it through the closure. Put it inside alesson
Lists, forms and data-driven viewsScroll large collections efficiently, give rows stable identity, build editable forms, add search and swipe actionslesson
Networking with URLSession and async/awaitDecode JSON with Codable, write typed async requests, model errors properly, cancel work when a view disappearslesson
Architecture: MVVM, observation and dependency injectionThe rule is not "always three layers". It is that a type should have one reason to change. A settings screen that onlylesson
Concurrency: actors, tasks and MainActorStructured concurrency with task groups, actor isolation that removes data races, Sendable checking, and the rules thatlesson
Localization, accessibility and system integrationShip text in many languages with String Catalogs, format numbers and dates per locale, support right-to-left layoutslesson
Security, privacy and data protectionStore secrets in the Keychain, enforce App Transport Security, declare data collection with privacy manifests, and uselesson
Testing, debugging and InstrumentsWrite fast unit tests with Swift Testing, drive the interface with XCUITest, use previews as a development tool, andlesson
Release engineering: TestFlight, CI and App Store ConnectFor a team, use App Store Connect API keys rather than an Apple ID password in CI. The key can be scoped, rotated andlesson
Next steps: widgets, watchOS, visionOS and the Swift ecosystemPin dependencies with a resolved file committed to the repository and update deliberately. A floating branch dependencylesson

Quick snippets

State and navigation

Choosing the right state container

import Observation

@Observable
final class Basket {
    var items: [String] = []
    var total: Int { items.count }

    func add(_ item: String) { items.append(item) }
}

struct BasketView: View {
    @State private var basket = Basket()

… 14 more lines in the full lesson.

Moving between screens

struct RootView: View {
    @State private var path: [Task] = []

    var body: some View {
        NavigationStack(path: $path) {
            List(tasks) { task in
                NavigationLink(value: task) {
                    TaskRow(task: task)
                }
            }
            .navigationTitle("Tasks")
            .navigationDestination(for: Task.self) { task in

… 8 more lines in the full lesson.

Full lesson: State and navigation →

Persistence and the App Store

Archive, upload, review

Bundle id      com.example.notes
Version        1.4.0        (user-visible, e.g. 1.4.0)
Build          12           (must increase for every upload)
Deployment     iOS 17.0
Devices        iPhone, iPad

Choosing where data lives

import SwiftData
import Foundation

@Model
final class Note {
    var title: String
    var createdAt: Date

    init(title: String, createdAt: Date = .now) {
        self.title = title
        self.createdAt = createdAt
    }

… 16 more lines in the full lesson.

Full lesson: Persistence and the App Store →

Swift essentials for iOS

Optionals and the three ways out

struct Profile {
    let name: String
    var nickname: String?          // may genuinely be absent
}

func badge(for profile: Profile) -> String {
    // 1. guard: leave early when a value is required to continue
    guard let nickname = profile.nickname, !nickname.isEmpty else {
        return profile.name
    }
    // 2. optional chaining plus nil-coalescing for a fallback
    let initial = profile.nickname?.first.map(String.init) ?? "?"

… 6 more lines in the full lesson.

Structs, classes and protocols

protocol PriceFormatting {
    func string(from amount: Decimal, currency: String) -> String
}

struct SimpleFormatter: PriceFormatting {
    func string(from amount: Decimal, currency: String) -> String {
        "\(amount) \(currency)"
    }
}

// extend the protocol instead of a base class: every conforming type gets this
extension PriceFormatting {

… 2 more lines in the full lesson.

Closures and errors

enum LoadError: LocalizedError {
    case offline
    case badStatus(Int)

    var errorDescription: String? {
        switch self {
        case .offline: return "You appear to be offline."
        case .badStatus(let code): return "The server returned \(code)."
        }
    }
}

… 13 more lines in the full lesson.

Full lesson: Swift essentials for iOS →

Layout, stacks and adaptive UI

Stacks, frames and alignment

struct MetricCard: View {
    let title: String
    let value: String

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text(title)
                .font(.caption)
                .foregroundStyle(.secondary)
            Text(value)
                .font(.title2.bold())
                .monospacedDigit()

… 16 more lines in the full lesson.

Grids and available space

struct PhotoGrid: View {
    let photos: [Photo]

    private let columns = [
        GridItem(.adaptive(minimum: 140), spacing: 8)
    ]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 8) {
                ForEach(photos) { photo in
                    Image(photo.name)

… 10 more lines in the full lesson.

Safe areas, size classes and text scaling

struct ReaderView: View {
    @Environment(\.horizontalSizeClass) private var sizeClass

    var body: some View {
        ScrollView {
            Text(bodyText)
                .font(.body)
                .dynamicTypeSize(...DynamicTypeSize.accessibility3)
                .padding(.horizontal, sizeClass == .regular ? 48 : 16)
        }
        .safeAreaInset(edge: .bottom) {
            Button("Next") { advance() }

… 6 more lines in the full lesson.

Full lesson: Layout, stacks and adaptive UI →

Lists, forms and data-driven views

A chart from the same data

import Charts

struct ProgressChart: View {
    let points: [(day: String, value: Int)]

    var body: some View {
        Chart(points, id: \.day) { point in
            BarMark(
                x: .value("Day", point.day),
                y: .value("Tasks", point.value)
            )
            .foregroundStyle(by: .value("Series", "Completed"))

… 5 more lines in the full lesson.

Full lesson: Lists, forms and data-driven views →

Networking with URLSession and async/await

Images, caching and retries

struct ArticleRow: View {
    let article: Article
    let model: ArticleListModel

    var body: some View {
        HStack(spacing: 12) {
            AsyncImage(url: URL(string: article.imageURL)) { phase in
                switch phase {
                case .success(let image):
                    image.resizable().scaledToFill()
                case .failure:
                    Image(systemName: "photo").foregroundStyle(.secondary)

… 14 more lines in the full lesson.

Full lesson: Networking with URLSession and async/await →

Architecture: MVVM, observation and dependency injection

Injecting for tests

final class InMemoryStore: CounterStoring {
    private var value = 0
    func load() -> Int { value }
    func save(_ newValue: Int) { value = newValue }
}

@Test
func incrementStoresTheNewValue() {
    let store = InMemoryStore()
    let model = CounterViewModel(store: store)

    model.increment()

… 5 more lines in the full lesson.

Full lesson: Architecture: MVVM, observation and dependency injection →

Concurrency: actors, tasks and MainActor

Tasks and structured concurrency

func loadAll(_ ids: [Int]) async throws -> [Summary] {
    try await withThrowingTaskGroup(of: Summary.self) { group in
        for id in ids {
            group.addTask { try await fetchSummary(id) }
        }
        var results: [Summary] = []
        for try await summary in group {
            results.append(summary)
        }
        return results
    }
}

… 6 more lines in the full lesson.

Actors and isolation

actor ImageCache {
    private var storage: [URL: Data] = [:]

    func data(for url: URL) -> Data? { storage[url] }

    func insert(_ data: Data, for url: URL) {
        storage[url] = data
    }
}

// actor state is only reachable through await
let cache = ImageCache()

… 8 more lines in the full lesson.

What actually breaks

// Value types composed of Sendable members are Sendable automatically
struct User: Sendable {
    let id: Int
    let name: String
}

// A class with mutable state is not, and the compiler will say so
// in Swift 6 language mode this is a hard error, not a warning:
final class TokenStore: @unchecked Sendable {
    private let lock = NSLock()
    private var token: String?
    func set(_ value: String) { lock.lock(); defer { lock.unlock() }; token = value }

… 2 more lines in the full lesson.

Full lesson: Concurrency: actors, tasks and MainActor →

Localization, accessibility and system integration

String Catalogs and formatting

// Localizable.xcstrings is a String Catalog; add languages in Xcode
Text("welcome_title")                       // looked up automatically
Text(verbatim: "v2.4.1")                    // never localise

// Plural variation is declared in the catalog, not in code
Text("\(count) items")                     // catalog entry with a plural rule

// Locale-aware formatting: never build these strings by hand
let price = Decimal(1299) / 100
Text(price, format: .currency(code: "GBP"))

let now = Date.now

… 2 more lines in the full lesson.

Right-to-left and VoiceOver

struct Row: View {
    let task: Task

    var body: some View {
        HStack {
            Image(systemName: task.done ? "checkmark.circle.fill" : "circle")
                .accessibilityHidden(true)          // decorative, the label carries meaning
            VStack(alignment: .leading) {
                Text(task.title).font(.body)
                Text(task.due, format: .dateTime.day().month())
                    .font(.caption).foregroundStyle(.secondary)
            }

… 8 more lines in the full lesson.

Extending into the system

import AppIntents

struct MarkDoneIntent: AppIntent {
    static var title: LocalizedStringResource = "Mark task done"
    static var openAppWhenRun = false

    @Parameter(title: "Task ID") var taskID: String

    func perform() async throws -> some IntentResult {
        try await TaskStore.shared.markDone(taskID)
        return .result()
    }

… 1 more lines in the full lesson.

Full lesson: Localization, accessibility and system integration →

Security, privacy and data protection

Permissions, entitlements and review

// Ask only when the feature is used, never on launch
import AVFoundation

func requestCamera() async -> Bool {
    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized: return true
    case .notDetermined: return await AVCaptureDevice.requestAccess(for: .video)
    default: return false
    }
}

Transport security and privacy manifests

<!-- Info.plist: do not weaken ATS globally -->
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>legacy.internal.example</key>
    <dict>
      <key>NSExceptionMinimumTLSVersion</key>
      <string>TLSv1.2</string>
      <key>NSIncludesSubdomains</key>
      <true/>
    </dict>

… 13 more lines in the full lesson.

Full lesson: Security, privacy and data protection →

Testing, debugging and Instruments

Finding leaks and slow frames

# run the app and attach Instruments from the command line
xcrun xctrace record --template "Leaks" \
  --device "iPhone 16" \
  --launch com.example.app \
  --output leaks.trace

# time profiler for a scroll-heavy screen
xcrun xctrace record --template "Time Profiler" \
  --launch com.example.app --output time.trace \
  --time-limit 60s

Unit tests with Swift Testing

import Testing

struct PriceFormatterTests {
    @Test("formats zero as a currency string")
    func formatsZero() {
        #expect(PriceFormatter().string(from: 0, currency: "GBP") == "0.00 GBP")
    }

    @Test("rejects negative stock", arguments: [-1, -100])
    func rejectsNegative(quantity: Int) {
        #expect(throws: CartError.self) {
            try CartItem(sku: "A1", quantity: quantity)

… 10 more lines in the full lesson.

UI tests and previews

import XCTest

final class CheckoutUITests: XCTestCase {
    func testAddingAnItemShowsItInTheCart() {
        let app = XCUIApplication()
        app.launchArguments = ["-uiTestSeed", "empty-cart"]
        app.launch()

        app.buttons["Add to cart"].firstMatch.tap()

        let badge = app.staticTexts["cart-count"]
        XCTAssertTrue(badge.waitForExistence(timeout: 5))

… 3 more lines in the full lesson.

Full lesson: Testing, debugging and Instruments →

Release engineering: TestFlight, CI and App Store Connect

Signing and provisioning

# archive and export exactly as CI would, from the command line
xcodebuild archive \
  -project App.xcodeproj \
  -scheme App -configuration Release \
  -destination "generic/platform=iOS" \
  -archivePath build/App.xcarchive

xcodebuild -exportArchive \
  -archivePath build/App.xcarchive \
  -exportOptionsPlist ExportOptions.plist \
  -exportPath build/ipa

fastlane and Xcode Cloud

# fastlane/Fastfile
default_platform(:ios)

platform :ios do
  desc "Run tests then upload a beta build"
  lane :beta do
    increment_build_number(
      build_number: ENV["GITHUB_RUN_NUMBER"] || "1"
    )
    run_tests(scheme: "App")
    build_app(
      scheme: "App",

… 9 more lines in the full lesson.

fastlane and Xcode Cloud

# .github/workflows/ios.yml
name: ios
on:
  push:
    branches: [main]
jobs:
  beta:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
      - name: Select Xcode
        run: sudo xcode-select -s /Applications/Xcode_16.app

… 10 more lines in the full lesson.

Full lesson: Release engineering: TestFlight, CI and App Store Connect →

Next steps: widgets, watchOS, visionOS and the Swift ecosystem

Extension targets and shared containers

import WidgetKit
import SwiftUI

struct Snapshot: TimelineEntry {
    let date: Date
    let openCount: Int
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> Snapshot {
        Snapshot(date: .now, openCount: 3)
    }

… 11 more lines in the full lesson.

Dependencies and cross-platform Swift

// Package.swift for a shared module used by the app and the watch app
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "Core",
    platforms: [.iOS(.v17), .watchOS(.v10), .macOS(.v14)],
    products: [
        .library(name: "Core", targets: ["Core"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0")

… 6 more lines in the full lesson.

Choosing what to learn next

// A design system is the highest-leverage shared package
public struct PrimaryButton: View {
    public let title: String
    public let action: () -> Void

    public init(title: String, action: @escaping () -> Void) {
        self.title = title
        self.action = action
    }

    public var body: some View {
        Button(title, action: action)

… 4 more lines in the full lesson.

Full lesson: Next steps: widgets, watchOS, visionOS and the Swift ecosystem →

FAQ

Is this iOS cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 13 lessons of the iOS course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full iOS course — it carries the worked explanations, the edge cases and the exercises behind every line here.

Android Flutter React Native Kotlin Swift

Last refreshed 2026-09-27.