axiom-swift-concurrency

0
1
Source

Use when you see 'actor-isolated', 'Sendable', 'data race', '@MainActor' errors, or when asking 'why is this not thread safe', 'how do I use async/await', 'what is @MainActor for', 'my app is crashing with concurrency errors', 'how do I fix data races' - Swift 6 strict concurrency patterns with actor isolation and async/await

Install

mkdir -p .claude/skills/axiom-swift-concurrency && curl -L -o skill.zip "https://mcp.directory/api/skills/download/4997" && unzip -o skill.zip -d .claude/skills/axiom-swift-concurrency && rm skill.zip

Installs to .claude/skills/axiom-swift-concurrency

About this skill

Swift 6 Concurrency Guide

Purpose: Progressive journey from single-threaded to concurrent Swift code Swift Version: Swift 6.0+, Swift 6.2+ for @concurrent iOS Version: iOS 17+ (iOS 18.2+ for @concurrent) Xcode: Xcode 16+ (Xcode 16.2+ for @concurrent) Context: WWDC 2025-268 "Embracing Swift concurrency" - approachable path to data-race safety

When to Use This Skill

Use this skill when:

  • Starting a new project and deciding concurrency strategy
  • Debugging Swift 6 concurrency errors (actor isolation, data races, Sendable warnings)
  • Deciding when to introduce async/await vs concurrency
  • Implementing @MainActor classes or async functions
  • Converting delegate callbacks to async-safe patterns
  • Deciding between @MainActor, nonisolated, @concurrent, or actor isolation
  • Resolving "Sending 'self' risks causing data races" errors
  • Making types conform to Sendable
  • Offloading CPU-intensive work to background threads
  • UI feels unresponsive and profiling shows main thread bottleneck

Do NOT use this skill for:

  • General Swift syntax (use Swift documentation)
  • SwiftUI-specific patterns (use axiom-swiftui-debugging or axiom-swiftui-performance)
  • API-specific patterns (use API documentation)

Core Philosophy: Start Single-Threaded

Apple's Guidance (WWDC 2025-268): "Your apps should start by running all of their code on the main thread, and you can get really far with single-threaded code."

The Progressive Journey

Single-Threaded → Asynchronous → Concurrent → Actors
     ↓                ↓             ↓           ↓
   Start here    Hide latency   Background   Move data
                 (network)      CPU work     off main

When to advance:

  1. Stay single-threaded if UI is responsive and operations are fast
  2. Add async/await when high-latency operations (network, file I/O) block UI
  3. Add concurrency when CPU-intensive work (image processing, parsing) freezes UI
  4. Add actors when too much main actor code causes contention

Key insight: Concurrent code is more complex. Only introduce concurrency when profiling shows it's needed.


Step 1: Single-Threaded Code (Start Here)

All code runs on the main thread by default in Swift 6.

// ✅ Simple, single-threaded
class ImageModel {
    var imageCache: [URL: Image] = [:]

    func fetchAndDisplayImage(url: URL) throws {
        let data = try Data(contentsOf: url)  // Reads local file
        let image = decodeImage(data)
        view.displayImage(image)
    }

    func decodeImage(_ data: Data) -> Image {
        // Decode image data
        return Image()
    }
}

Main Actor Mode (Xcode 26+):

  • Enabled by default for new projects
  • All code protected by @MainActor unless explicitly marked otherwise
  • Access shared state safely without worrying about concurrent access

Build Setting (Xcode 26+):

Build Settings → Swift Compiler — Language
→ "Default Actor Isolation" = Main Actor

Build Settings → Swift Compiler — Upcoming Features
→ "Approachable Concurrency" = Yes

When this is enough: If all operations are fast (<16ms for 60fps), stay single-threaded!


Step 2: Asynchronous Tasks (Hide Latency)

Add async/await when waiting on data (network, file I/O) would freeze UI.

Problem: Network Access Blocks UI

// ❌ Blocks main thread until network completes
func fetchAndDisplayImage(url: URL) throws {
    let (data, _) = try URLSession.shared.data(from: url)  // ❌ Freezes UI!
    let image = decodeImage(data)
    view.displayImage(image)
}

Solution: Async/Await

// ✅ Suspends without blocking main thread
func fetchAndDisplayImage(url: URL) async throws {
    let (data, _) = try await URLSession.shared.data(from: url)  // ✅ Suspends here
    let image = decodeImage(data)  // ✅ Resumes here when data arrives
    view.displayImage(image)
}

What happens:

  1. Function starts on main thread
  2. await suspends function without blocking main thread
  3. URLSession fetches data on background thread (library handles this)
  4. Function resumes on main thread when data arrives
  5. UI stays responsive the entire time

Task Creation

Create tasks in response to user events:

class ImageModel {
    var url: URL = URL(string: "https://swift.org")!

    func onTapEvent() {
        Task {  // ✅ Create task for user action
            do {
                try await fetchAndDisplayImage(url: url)
            } catch {
                displayError(error)
            }
        }
    }
}

Task Interleaving (Important Concept)

Multiple async tasks can run on the same thread by taking turns:

Task 1: [Fetch Image] → (suspend) → [Decode] → [Display]
Task 2: [Fetch News]  → (suspend) → [Display News]

Main Thread Timeline:
[Fetch Image] → [Fetch News] → [Decode Image] → [Display Image] → [Display News]

Benefits:

  • Main thread never sits idle
  • Tasks make progress as soon as possible
  • No concurrency yet—still single-threaded!

When to use tasks:

  • High-latency operations (network, file I/O)
  • Library APIs handle background work for you (URLSession, FileManager)
  • Your own code stays on main thread

Step 3: Concurrent Code (Background Threads)

Add concurrency when CPU-intensive work blocks UI.

Problem: Decoding Blocks UI

Profiling shows decodeImage() takes 200ms, causing UI glitches:

func fetchAndDisplayImage(url: URL) async throws {
    let (data, _) = try await URLSession.shared.data(from: url)
    let image = decodeImage(data)  // ❌ 200ms on main thread!
    view.displayImage(image)
}

Solution 1: @concurrent Attribute (Swift 6.2+)

Forces function to always run on background thread:

func fetchAndDisplayImage(url: URL) async throws {
    let (data, _) = try await URLSession.shared.data(from: url)
    let image = await decodeImage(data)  // ✅ Runs on background thread
    view.displayImage(image)
}

@concurrent
func decodeImage(_ data: Data) async -> Image {
    // ✅ Always runs on background thread pool
    // Good for: image processing, file I/O, parsing
    return Image()
}

What @concurrent does:

  • Function always switches to background thread pool
  • Compiler highlights main actor data access (shows what you need to fix)
  • Cannot access @MainActor properties without await

Requirements: Swift 6.2, Xcode 16.2+, iOS 18.2+

Solution 2: nonisolated (Library APIs)

If providing a general-purpose API, use nonisolated instead:

// ✅ Stays on caller's actor
nonisolated
func decodeImage(_ data: Data) -> Image {
    // Runs on whatever actor called it
    // Main actor → stays on main actor
    // Background → stays on background
    return Image()
}

When to use nonisolated:

  • Library APIs where caller decides where work happens
  • Small operations that might be OK on main thread
  • General-purpose code used in many contexts

When to use @concurrent:

  • Operations that should always run on background (image processing, parsing)
  • Performance-critical work that shouldn't block UI

Breaking Ties to Main Actor

When you mark a function @concurrent, compiler shows main actor access:

@MainActor
class ImageModel {
    var cachedImage: [URL: Image] = [:]  // Main actor data

    @concurrent
    func decodeImage(_ data: Data, at url: URL) async -> Image {
        if let image = cachedImage[url] {  // ❌ Error: main actor access!
            return image
        }
        // decode...
    }
}

Strategy 1: Move to caller (keep work synchronous):

func fetchAndDisplayImage(url: URL) async throws {
    // ✅ Check cache on main actor BEFORE async work
    if let image = cachedImage[url] {
        view.displayImage(image)
        return
    }

    let (data, _) = try await URLSession.shared.data(from: url)
    let image = await decodeImage(data)  // No URL needed now
    view.displayImage(image)
}

@concurrent
func decodeImage(_ data: Data) async -> Image {
    // ✅ No main actor access needed
    return Image()
}

Strategy 2: Use await (access main actor asynchronously):

@concurrent
func decodeImage(_ data: Data, at url: URL) async -> Image {
    // ✅ Await to access main actor data
    if let image = await cachedImage[url] {
        return image
    }
    // decode...
}

Strategy 3: Make nonisolated (if doesn't need actor):

nonisolated
func decodeImage(_ data: Data) -> Image {
    // ✅ No actor isolation, can call from anywhere
    return Image()
}

Concurrent Thread Pool

When work runs on background:

Main Thread:    [UI] → (suspend) → [UI Update]
                         ↓
Background Pool: [Task A] → [Task B] → [Task A resumes]
                 Thread 1    Thread 2    Thread 3

Key points:

  • System manages thread pool size (1-2 threads on Watch, many on Mac)
  • Task can resume on different thread than it started
  • You never specify which thread—system optimizes automatically

Step 4: Actors (Move Data Off Main Thread)

Add actors when too much code runs on main actor causing contention.

Problem: Main Actor Contention

@MainActor
class ImageModel {
    var cachedImage: [URL: Image] = [:]
    let networkManager: NetworkManager = NetworkManager()  // ❌ Also @MainActor

    func fetchAndDisplayImage(url: URL) async throws {
        // ✅ Background work...
        let connection = await networkManager.openConnection(for: url)  // ❌ Hops to main!
        let data = try await connection.data(from: url)
        await networkManager.closeConnection(connection, for: url)  // ❌ Hops to main!

        let image = await decodeImage(data)
        view.displayImage(image)
    }
}

Issue: Background task keeps hopping to main actor for network manager access.

Solution: Network Manager Actor

// ✅ Move network state off main actor
actor NetworkManager {


---

*Content truncated.*

axiom-swiftui-nav-diag

CharlesWiltgen

Use when debugging navigation not responding, unexpected pops, deep links showing wrong screen, state lost on tab switch or background, crashes in navigationDestination, or any SwiftUI navigation failure - systematic diagnostics with production crisis defense

54

axiom-swiftui-26-ref

CharlesWiltgen

Use when implementing iOS 26 SwiftUI features - covers Liquid Glass design system, performance improvements, @Animatable macro, 3D spatial layout, scene bridging, WebView/WebPage, AttributedString rich text editing, drag and drop enhancements, and visionOS integration for iOS 26+

33

axiom-extensions-widgets-ref

CharlesWiltgen

Use when implementing widgets, Live Activities, Control Center controls, or app extensions - comprehensive API reference for WidgetKit, ActivityKit, App Groups, and extension lifecycle for iOS 14+

13

axiom-ios-build

CharlesWiltgen

Use when ANY iOS build fails, test crashes, Xcode misbehaves, or environment issue occurs before debugging code. Covers build failures, compilation errors, dependency conflicts, simulator problems, environment-first diagnostics.

253

axiom-camera-capture-ref

CharlesWiltgen

Reference — AVCaptureSession, AVCapturePhotoSettings, AVCapturePhotoOutput, RotationCoordinator, photoQualityPrioritization, deferred processing, AVCaptureMovieFileOutput, session presets, capture device APIs

42

axiom-swiftdata

CharlesWiltgen

Use when working with SwiftData - @Model definitions, @Query in SwiftUI, @Relationship macros, ModelContext patterns, CloudKit integration, iOS 26+ features, and Swift 6 concurrency with @MainActor — Apple's native persistence framework

12

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

1,6851,428

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

1,2641,326

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

1,5331,147

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

1,355809

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

1,264727

pdf-to-markdown

aliceisjustplaying

Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.

1,483684