Irbisa · Cheatsheet
Swift 6 Strict Concurrency
Isolation domains, Sendable, global actors, region isolation, sending, Swift 5 to 6 migration
What does Swift 6 strict concurrency checking verify, and how is the language mode different from the compiler version?
// swift-tools-version: 6.0// Tools 6.0 = Swift 6 compiler; targets default to language mode 6.import PackageDescription let package = Package( name: "App",…What is an isolation domain in Swift, and how can a value move from one domain to another?
Why does a class with only
letproperties still fail to beSendable, and when is@unchecked Sendablethe honest fix?import Foundationimport Synchronization // Rejected, one reason each:// class A: Sendable { let x = 1 } // not final// final class B: Sendable { var x = 1 } // mutable stored property…Swift 6 rejects both declarations in
Services. What is each error saying, and what are the legal shapes for shared mutable state?final class Analytics { var queue: [String] = []} enum Services { static var currentUserID: String? // (1) static let analytics = Analytics() // (2)}What does
nonisolated(unsafe)promise the compiler, and when is it an honest answer rather than a way to silence an error?import Foundationimport Synchronization enum Metrics { private static let lock = NSLock() // Invariant: read and written only while `lock` is held.…Why can a class holding a
MutexbeSendablewithout@unchecked, and what are the restrictions ofMutex?import Synchronizationimport os // iOS 18+: the dictionary is reachable only through the lock.final class TokenCache: Sendable { private let tokens = Mutex<[String: String]>([:])…Find the bug: this
Sendablestats class compiles clean in Swift 6, yet under load the counts come out lower than the number ofrecordcalls.final class RequestStats: Sendable { private let storage = Mutex<[String: Int]>([:]) var counts: [String: Int] { get { storage.withLock { $0 } } set { storage.withLock { $0 = newValue } } } func record(_ host: String) { counts[host, default: 0] += 1 } func reset() { counts = [:] }}What is a global actor, and when would you define your own instead of using
@MainActoror a plainactor?@globalActoractor StorageActor { static let shared = StorageActor()} @StorageActor…What does
nonisolatedmean on a stored property, a synchronous method and anasyncmethod, and does it move work off the actor?@MainActorfinal class Profile: Hashable { nonisolated let id: UUID // Sendable let: readable from any domain var name: String // main-actor state init(id: UUID, name: String) { self.id = id; self.name = name }…Does this compile in Swift 6 language mode, and what changes if you delete the
printline?final class Report { // not Sendable var title = ""} actor Archive { private var reports: [Report] = [] func store(_ r: Report) { reports.append(r) }} func archiveWeekly(_ archive: Archive) async { let report = Report() report.title = "Weekly" await archive.store(report) print(report.title)}What can a
sendingparameter express that@Sendablecannot, and why doTask.initandaddTasktakesendingclosures?final class Draft { var body = "" } // not Sendable actor Outbox { private var queued: [Draft] = [] func queue(_ d: Draft) { queued.append(d) }}…After the target turned on Approachable Concurrency, scrolling stutters while this model decodes images. Why, and how do you fix it?
@MainActorfinal class GalleryModel { var thumbnails: [UIImage] = [] func load(_ files: [Data]) async { for data in files { if let image = await Self.decode(data) { thumbnails.append(image) } } } nonisolated static func decode(_ data: Data) async -> UIImage? { UIImage(data: data)?.preparingForDisplay() }}How do you migrate a large app from Swift 5 to the Swift 6 language mode without freezing feature work?
// Package.swift: one target at a time, leaves first.let package = Package( name: "App", targets: [ .target( name: "CoreModels",…What do Approachable Concurrency and default
MainActorisolation change in a target, and why keep them off in a shared framework?Your
@MainActorview model must conform toEquatableand the compiler rejects the conformance. Why, and what are your options?@MainActorfinal class Model { let id: Int var rows: [String] = [] init(id: Int) { self.id = id }}…What does
@preconcurrencydo on an import and on a protocol conformance, and what risk remains after the build goes green?@preconcurrency import LegacyPlayer // stop diagnosing its missing Sendable @MainActorfinal class PlayerBox: NSObject { private let player = LegacyPlayer() private var finished = 0…A vendor callback is documented to arrive on the main thread but the method is
nonisolated. When isMainActor.assumeIsolatedright, and when does it crash?@MainActorfinal class Player { private var progress: Double = 0 // SDK requirement is nonisolated; docs say "always called on the main thread". nonisolated func vendorDidTick(_ value: Double) {…A vendor's Objective-C SDK calls your delegate on arbitrary threads. How do you bring it into a Swift 6 module without
@unchecked Sendableeverywhere?final class ScannerBridge: NSObject, VendorScannerDelegate, Sendable { enum Event: Sendable { case found(String) } let events: AsyncStream<Event> private let continuation: AsyncStream<Event>.Continuation override init() { (events, continuation) = AsyncStream.makeStream(of: Event.self)…Your target compiles clean in Swift 6 mode, yet Thread Sanitizer reports a data race. What can the compiler not see, and how do you track the race down?