axiom-swift-concurrency

0
0
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-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.

91

axiom-getting-started

CharlesWiltgen

Use when first installing Axiom, unsure which skill to use, want an overview of available skills, or need help finding the right skill for your situation — interactive onboarding that recommends skills based on your project and current focus

00

axiom-ui-testing

CharlesWiltgen

Use when writing UI tests, recording interactions, tests have race conditions, timing dependencies, inconsistent pass/fail behavior, or XCTest UI tests are flaky - covers Recording UI Automation (WWDC 2025), condition-based waiting, network conditioning, multi-factor testing, crash debugging, and accessibility-first testing patterns

00

axiom-core-spotlight-ref

CharlesWiltgen

Use when indexing app content for Spotlight search, using NSUserActivity for prediction/handoff, or choosing between CSSearchableItem and IndexedEntity - covers Core Spotlight framework and NSUserActivity integration for iOS 9+

00

axiom-vision-diag

CharlesWiltgen

subject not detected, hand pose missing landmarks, low confidence observations, Vision performance, coordinate conversion, VisionKit errors, observation nil, text not recognized, barcode not detected, DataScannerViewController not working, document scan issues

00

axiom-now-playing-carplay

CharlesWiltgen

CarPlay Now Playing integration patterns. Use when implementing CarPlay audio controls, CPNowPlayingTemplate customization, or debugging CarPlay-specific issues.

00

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.

643969

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.

591705

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."

318398

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.

339397

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.

451339

fastapi-templates

wshobson

Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.

304231

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.