Swift 6.2 concurrency updates including default MainActor inference, @concurrent for background work, isolated conformances, and approachable concurrency migration. Use when adopting Swift 6.2 concurrency features or fixing data-race errors.
npx skills add https://github.com/rshankras/claude-code-apple-skills --skill swift-concurrency-updates
Swift 6.2 introduces "Approachable Concurrency" -- a set of changes that make strict concurrency dramatically easier to adopt. Code runs on @MainActor by default, async functions stay on the calling actor, and you explicitly request background execution with @concurrent.
This skill covers only the Swift 6.2 specific changes. For general concurrency patterns (actors, TaskGroup, AsyncSequence, Sendable, cancellation), see the swift/concurrency-patterns skill.
@concurrent, isolated conformances, or approachable concurrency| Feature | Before (6.0/6.1) | After (6.2) |
|---------|------------------|-------------|
| Default isolation | Nothing inferred; manual @MainActor everywhere | Opt-in mode infers @MainActor on everything |
| Async function execution | Hops to generic concurrent executor | Stays on calling actor |
| @MainActor type conforming to protocol | Compiler error for non-isolated protocols | Isolated conformances: @MainActor Protocol |
| Background execution | Task.detached or manual nonisolated functions | @concurrent attribute |
| Global/static mutable state | Required @MainActor annotation or Sendable | Default MainActor mode handles it automatically |
In Swift 6.2, async functions without specific actor isolation stay on whatever actor called them, instead of hopping to the generic concurrent executor as in 6.0/6.1 -- eliminating a common source of data-race errors with no code changes required.
An opt-in build setting that makes all code implicitly @MainActor unless explicitly opted out with nonisolated.
Xcode: Build Settings > Swift Compiler - Concurrency > "Default Actor Isolation" > "MainActor"
Swift Package Manager:
.executableTarget(
name: "MyApp",
swiftSettings: [
.defaultIsolation(MainActor.self)
]
)
With this enabled, app-level types no longer need explicit @MainActor annotations -- including global/static mutable state, which otherwise needs an explicit @MainActor static let ....
| Target Type | Recommended? | Reason |
|-------------|-------------|--------|
| App target | Yes | Apps are UI-driven; most code belongs on MainActor |
| Script / executable | Yes | Scripts are sequential; MainActor default is natural |
| Library / framework | No | Libraries must not impose actor isolation on consumers |
| Package plugin | No | Same reasoning as libraries |
When a type or function genuinely needs to run off the main actor, mark it nonisolated:
nonisolated struct ImageProcessor {
func processImage(_ data: Data) -> UIImage {
// Runs on any thread, not MainActor
...
}
}
Isolated conformances let a @MainActor type conform to a protocol that does not require actor isolation, using extension Type: @MainActor ProtocolName. Before Swift 6.2, this produced a compiler error about the main actor-isolated conformance crossing an isolation boundary.
protocol Exportable {
func export()
}
@MainActor
final class StickerModel {
let processor: PhotoProcessor
func doExport() { processor.exportAsPNG() }
}
// ✅ Swift 6.2 -- isolated conformance
extension StickerModel: @MainActor Exportable {
func export() {
processor.exportAsPNG() // Works: conformance is MainActor-isolated
}
}
The conformance can only be used from a context that shares the same isolation domain:
// ✅ Used within @MainActor context -- OK
@MainActor
func exportAll(_ items: [any Exportable]) {
for item in items { item.export() }
}
// ❌ Used outside @MainActor -- compile error
nonisolated func exportAll(_ items: [any Exportable]) {
for item in items {
item.export() // Error: isolated conformance not available here
}
}
@concurrent explicitly offloads a function to the background thread pool, replacing the pattern of using Task.detached for compute-intensive operations.
nonisolated struct ImageProcessor {
@concurrent
func resize(image: Data, to size: CGSize) async -> Data {
// Runs on background thread pool
...
}
}
// Caller (on MainActor):
let resized = await ImageProcessor().resize(image: data, to: targetSize)
nonisolated (for structs/classes; actors are already isolated)@concurrent to the functionasyncawait| Mechanism | Use Case | Structured? |
|-----------|----------|-------------|
| @concurrent | Single function that must run on background thread | Yes (inherits task context) |
| Task.detached | Fire-and-forget background work, no structured parent | No |
| actor | Shared mutable state needing serialized access | N/A (isolation, not scheduling) |
| Task {} | Unstructured task inheriting current actor | No |
Reserve it for CPU-intensive work, blocking I/O that would freeze the UI, or work measured to be long enough to justify the thread hop -- not trivial functions:
// ❌ Wrong -- trivial work does not need @concurrent
nonisolated struct UserFormatter {
@concurrent
func formatName(_ user: User) async -> String {
return "\(user.firstName) \(user.lastName)"
}
}
// ✅ Right -- leave it on the calling actor
struct UserFormatter {
func formatName(_ user: User) -> String {
return "\(user.firstName) \(user.lastName)"
}
}
Step 1: Update to Swift 6.2 toolchain
Ensure your Xcode version supports Swift 6.2 and your project's Swift language version is set to 6.2.
Step 2: Enable default MainActor inference (for app targets)
See "Enabling It" above -- the Xcode build setting or .defaultIsolation(MainActor.self) in swiftSettings.
Step 3: Remove redundant @MainActor annotations
With default MainActor inference enabled, explicit @MainActor annotations on app-level types are redundant and can be removed to reduce noise.
Step 4: Replace Task.detached with @concurrent where appropriate
Mark the offloaded function @concurrent directly instead of wrapping it in a detached task inside a TaskGroup.
Step 5: Fix remaining conformance errors with isolated conformances
Part of migrating to Swift 6.2 involves replacing unsafe @unchecked Sendable workarounds with isolated conformances:
// Before -- unsafe workaround
extension MyModel: @unchecked Sendable {}
// After -- isolated conformance
extension MyModel: @MainActor Exportable {
func export() { ... }
}
Step 6: Add nonisolated to types/functions that must not be on MainActor
With default MainActor inference, anything not explicitly marked nonisolated runs on MainActor. Audit background data processing types, network parsers, file I/O utilities, and computation-heavy algorithms.
nonisolated struct JSONParser {
@concurrent
func parse(_ data: Data) async throws -> [Model] { ... }
}
~/Downloads/docs/Swift-Concurrency-Updates.md — read if present; skip silently if absent.Swift 6.2 Concurrency Defaults
+--------------------------------------------+
| Everything is @MainActor by default |
| (with "infer main actor" build setting) |
| |
| Async functions stay on the calling actor |
| (no implicit hop to background) |
| |
| Use @concurrent to explicitly go background|
| Use nonisolated to opt out of MainActor |
+--------------------------------------------+
Progression:
1. Write code -> runs on MainActor -> no data races
2. Use async/await -> stays on calling actor -> still no races
3. Need parallelism -> @concurrent -> explicit, auditable
4. Need shared state -> actor -> serialized access
Libraries should let consumers choose their own isolation strategy. Only app targets and executables should use default MainActor inference.
// ❌ Wrong -- library imposes MainActor on all consumers
// Package.swift
.target(
name: "MyNetworkingLib",
swiftSettings: [
.defaultIsolation(MainActor.self) // Do NOT do this for libraries
]
)
Every @concurrent call involves a thread hop. Only use it for genuinely expensive work (see "When NOT to Use @concurrent" above).
// With default MainActor inference enabled:
// ❌ Wrong -- this CPU-intensive parser now runs on MainActor, blocking UI
struct LargeFileParser {
func parse(_ data: Data) -> [Record] {
...
}
}
// ✅ Right -- opt out of MainActor for background-suitable types
nonisolated struct LargeFileParser {
@concurrent
func parse(_ data: Data) async -> [Record] {
...
}
}
See "Isolated Conformances" above -- the conformance is only usable from a context that matches its isolation.
// ❌ Wrong -- removed annotations but did NOT enable default MainActor inference
class ViewModel { // No longer @MainActor -- state is unprotected
var items: [Item] = []
func load() async { ... }
}
// ✅ Right -- either keep the annotation...
@MainActor
class ViewModel {
var items: [Item] = []
func load() async { ... }
}
// ...or enable "Default Actor Isolation: MainActor" in build settings
// and then annotations are unnecessary
class ViewModel {
var items: [Item] = []
func load() async { ... }
}
When reviewing code that uses or should use Swift 6.2 concurrency features:
.defaultIsolation(MainActor.self)@MainActor annotations removed (if default MainActor inference is enabled)nonisolated applied to types/functions that must run off the main actornonisolated and uses @concurrent@concurrent only used for genuinely expensive operations@concurrent functions are asyncnonisolated (for structs/classes)Task.detached replaced with @concurrent where structured concurrency is preferable@MainActor Protocol syntax used for MainActor types conforming to non-isolated protocols@unchecked Sendable workarounds that isolated conformances can replace@unchecked Sendable conformances from pre-6.2 workaroundsTask.detached calls (prefer @concurrent or structured concurrency)swift/concurrency-patternsswift/concurrency-patterns/actors-and-isolation.mdswift/concurrency-patterns/structured-concurrency.mdswift/concurrency-patterns/migration-guide.md~/Downloads/docs/Swift-Concurrency-Updates.md — read if present; skip silently if absent.Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
Access NCBI GEO for gene expression/genomics data. Search/download microarray and RNA-seq datasets (GSE, GSM, GPL), retrieve SOFT/Matrix files, for transcriptomics and expression analysis.
Bayesian modeling with PyMC. Build hierarchical models, MCMC (NUTS), variational inference, LOO/WAIC comparison, posterior checks, for probabilistic programming and inference.
Multi-objective optimization framework. NSGA-II, NSGA-III, MOEA/D, Pareto fronts, constraint handling, benchmarks (ZDT, DTLZ), for engineering design and optimization problems.
Statistical modeling toolkit. OLS, GLM, logistic, ARIMA, time series, hypothesis tests, diagnostics, AIC/BIC, for rigorous statistical inference and econometric analysis.
Add unsigned integer (uint) type support to PyTorch operators by updating AT_DISPATCH macros. Use when adding support for uint16, uint32, uint64 types to operators, kernels, or when user mentions enabling unsigned types, barebones unsigned types, or uint support.
Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.
Write docstrings for PyTorch functions and methods following PyTorch conventions. Use when writing or updating docstrings in PyTorch code.
Take rshankras/swift-concurrency-updates from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.