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
| Topic | What it covers | |
|---|---|---|
| State and navigation | SwiftUI is a function of state: change the state, and the framework recomputes the affected views. Picking the wrong | lesson |
| Persistence and the App Store | Reads and writes to a @Model are main-actor friendly for small apps, but heavy imports and background sync belong on a | lesson |
| Swift essentials for iOS | SwiftUI views are structs precisely because value semantics make diffing cheap and predictable. Protocols add the | lesson |
| Layout, stacks and adaptive UI | GeometryReader is greedy: it expands to the space offered and reports it through the closure. Put it inside a | lesson |
| Lists, forms and data-driven views | Scroll large collections efficiently, give rows stable identity, build editable forms, add search and swipe actions | lesson |
| Networking with URLSession and async/await | Decode JSON with Codable, write typed async requests, model errors properly, cancel work when a view disappears | lesson |
| Architecture: MVVM, observation and dependency injection | The rule is not "always three layers". It is that a type should have one reason to change. A settings screen that only | lesson |
| Concurrency: actors, tasks and MainActor | Structured concurrency with task groups, actor isolation that removes data races, Sendable checking, and the rules that | lesson |
| Localization, accessibility and system integration | Ship text in many languages with String Catalogs, format numbers and dates per locale, support right-to-left layouts | lesson |
| Security, privacy and data protection | Store secrets in the Keychain, enforce App Transport Security, declare data collection with privacy manifests, and use | lesson |
| Testing, debugging and Instruments | Write fast unit tests with Swift Testing, drive the interface with XCUITest, use previews as a development tool, and | lesson |
| Release engineering: TestFlight, CI and App Store Connect | For a team, use App Store Connect API keys rather than an Apple ID password in CI. The key can be scoped, rotated and | lesson |
| Next steps: widgets, watchOS, visionOS and the Swift ecosystem | Pin dependencies with a resolved file committed to the repository and update deliberately. A floating branch dependency | lesson |
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?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
Android Flutter React Native Kotlin Swift
Last refreshed 2026-09-27.