Claude Code for Swift Concurrency: Async/Await, Actors, and Structured Concurrency — Claude Skills 360 Blog
Blog / Mobile / Claude Code for Swift Concurrency: Async/Await, Actors, and Structured Concurrency
Mobile

Claude Code for Swift Concurrency: Async/Await, Actors, and Structured Concurrency

Published: January 15, 2027
Read time: 9 min read
By: Claude Skills 360

Swift’s structured concurrency eliminates callback pyramids and race conditions through the type system. async/await suspends without blocking threads. actor types serialize access to mutable state — the compiler enforces isolation. TaskGroup launches child tasks with automatic cancellation propagation. AsyncStream bridges delegate callbacks and notification center to async iteration. Swift 6’s strict concurrency checking makes data races compile-time errors. @MainActor isolates UI updates to the main thread with compile-time enforcement. Claude Code generates Swift async functions, actor types, TaskGroup patterns, AsyncStream bridges, and the SwiftUI async data loading patterns for production iOS and macOS apps.

CLAUDE.md for Swift Concurrency

## Swift Concurrency Stack
- Swift >= 5.10 (strict concurrency available), targeting Swift 6 semantics
- Enable: SWIFT_STRICT_CONCURRENCY = complete in Xcode build settings
- async/await: use for all I/O, network, and disk operations
- Actor: use for mutable shared state — never use DispatchQueue.main.async
- MainActor: @MainActor for all view models and UI-observable state
- Task: structured (TaskGroup) preferred over unstructured (Task.detached)
- Sendable: all types crossing actor boundaries must be Sendable

Async/Await Fundamentals

// Services/OrderService.swift — async service layer
import Foundation

struct Order: Codable, Sendable, Identifiable {
    let id: String
    let customerId: String
    let amount: Int
    let status: OrderStatus
    let createdAt: Date
}

enum OrderStatus: String, Codable, Sendable {
    case pending, processing, shipped, delivered, cancelled
}

enum OrderError: Error {
    case notFound
    case networkError(Error)
    case invalidResponse
    case unauthorized
}

actor OrderService {
    private let baseURL: URL
    private let session: URLSession
    private var cache: [String: Order] = [:]

    init(baseURL: URL, session: URLSession = .shared) {
        self.baseURL = baseURL
        self.session = session
    }

    // async throws: can fail asynchronously
    func fetchOrder(id: String) async throws -> Order {
        // Check cache first
        if let cached = cache[id] {
            return cached
        }

        let url = baseURL.appendingPathComponent("orders/\(id)")
        var request = URLRequest(url: url)
        request.setValue("Bearer \(await AuthService.shared.token)", forHTTPHeaderField: "Authorization")

        let (data, response) = try await session.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw OrderError.invalidResponse
        }

        switch httpResponse.statusCode {
        case 200:
            let order = try JSONDecoder().decode(Order.self, from: data)
            cache[order.id] = order
            return order
        case 401:
            throw OrderError.unauthorized
        case 404:
            throw OrderError.notFound
        default:
            throw OrderError.invalidResponse
        }
    }

    func clearCache() {
        cache.removeAll()
    }
}

Actor Isolation

// Actors/OrderStore.swift — actor for thread-safe mutable state
import Observation

// @Observable macro (Swift 5.9+) works with @MainActor
@MainActor
@Observable
final class OrderStore {
    var orders: [Order] = []
    var isLoading = false
    var error: String?

    private let service: OrderService

    init(service: OrderService) {
        self.service = service
    }

    // Called on MainActor — safe to mutate @Observable properties
    func loadOrders() async {
        isLoading = true
        error = nil

        do {
            let fetched = try await service.fetchAllOrders()
            orders = fetched
        } catch {
            self.error = error.localizedDescription
        }

        isLoading = false
    }

    func cancelOrder(id: String) async {
        do {
            let updated = try await service.cancelOrder(id: id)
            if let index = orders.firstIndex(where: { $0.id == id }) {
                orders[index] = updated
            }
        } catch {
            self.error = "Failed to cancel: \(error.localizedDescription)"
        }
    }
}


// Non-UI actor: protects shared mutable state from concurrent access
actor RateLimiter {
    private var requestCount = 0
    private var windowStart = Date()
    private let maxRequests: Int
    private let windowSeconds: TimeInterval

    init(maxRequests: Int = 100, windowSeconds: TimeInterval = 60) {
        self.maxRequests = maxRequests
        self.windowSeconds = windowSeconds
    }

    func checkAndIncrement() throws {
        let now = Date()

        // Reset window if expired
        if now.timeIntervalSince(windowStart) > windowSeconds {
            requestCount = 0
            windowStart = now
        }

        guard requestCount < maxRequests else {
            throw RateLimitError.exceeded
        }

        requestCount += 1
    }
}

TaskGroup for Parallel Work

// Concurrency/ParallelFetcher.swift — structured concurrent work
import Foundation

func fetchOrdersBatch(ids: [String], service: OrderService) async -> [String: Result<Order, Error>] {
    await withTaskGroup(of: (String, Result<Order, Error>).self) { group in
        // Launch concurrent child tasks
        for id in ids {
            group.addTask {
                do {
                    let order = try await service.fetchOrder(id: id)
                    return (id, .success(order))
                } catch {
                    return (id, .failure(error))
                }
            }
        }

        // Collect results — order not guaranteed
        var results: [String: Result<Order, Error>] = [:]
        for await (id, result) in group {
            results[id] = result
        }
        return results
    }
}

// withThrowingTaskGroup: cancel all if any throws
func fetchAllRequired(ids: [String], service: OrderService) async throws -> [Order] {
    try await withThrowingTaskGroup(of: Order.self) { group in
        for id in ids {
            group.addTask {
                try await service.fetchOrder(id: id)
            }
        }

        var orders: [Order] = []
        for try await order in group {
            orders.append(order)
        }
        return orders
    }
}

AsyncStream

// Streams/OrderStream.swift — bridge callbacks to AsyncSequence
import Foundation

extension OrderService {
    // Bridge URLSession WebSocket to AsyncStream
    func orderUpdates(for orderId: String) -> AsyncStream<Order> {
        AsyncStream { continuation in
            let task = Task {
                let url = URL(string: "wss://api.example.com/orders/\(orderId)/stream")!
                let (stream, _) = try await URLSession.shared.webSocketTask(with: url)

                do {
                    while !Task.isCancelled {
                        let message = try await stream.receive()

                        switch message {
                        case .string(let text):
                            if let data = text.data(using: .utf8),
                               let order = try? JSONDecoder().decode(Order.self, from: data) {
                                continuation.yield(order)

                                if order.status == .delivered || order.status == .cancelled {
                                    continuation.finish()
                                    return
                                }
                            }
                        case .data(let data):
                            if let order = try? JSONDecoder().decode(Order.self, from: data) {
                                continuation.yield(order)
                            }
                        @unknown default:
                            break
                        }
                    }
                } catch {
                    continuation.finish()
                }
            }

            // Called when consumer cancels
            continuation.onTermination = { _ in
                task.cancel()
            }
        }
    }
}

// Usage: iterate over live order status updates
func trackOrder(id: String) async {
    for await order in await orderService.orderUpdates(for: id) {
        print("Order \(id) status: \(order.status)")
    }
    print("Tracking complete")
}

SwiftUI Integration

// Views/OrderDetailView.swift — modern SwiftUI async patterns
import SwiftUI

struct OrderDetailView: View {
    let orderId: String
    @Environment(OrderStore.self) private var store
    @State private var order: Order?
    @State private var isLoading = false
    @State private var error: String?

    var body: some View {
        Group {
            if isLoading {
                ProgressView()
            } else if let order {
                OrderContent(order: order)
            } else if let error {
                ContentUnavailableView(error, systemImage: "exclamationmark.triangle")
            }
        }
        .task {
            // task modifier: auto-cancelled when view disappears
            await loadOrder()
        }
        .refreshable {
            await loadOrder()
        }
    }

    private func loadOrder() async {
        isLoading = true
        defer { isLoading = false }

        do {
            order = try await store.service.fetchOrder(id: orderId)
        } catch {
            self.error = error.localizedDescription
        }
    }
}

struct OrderListView: View {
    @Environment(OrderStore.self) private var store

    var body: some View {
        List(store.orders) { order in
            NavigationLink(value: order) {
                OrderRowView(order: order)
            }
        }
        .overlay {
            if store.isLoading && store.orders.isEmpty {
                ProgressView("Loading orders...")
            }
        }
        .task {
            await store.loadOrders()
        }
    }
}

For the iOS UIKit patterns with completion handlers and delegates that Swift concurrency replaces, see the iOS Swift guide for UIKit integration with withCheckedContinuation. For the Flutter Dart async/await approach that provides similar structured concurrency on the mobile side, the Flutter advanced guide covers Riverpod async state management. The Claude Skills 360 bundle includes Swift concurrency skill sets covering actors, TaskGroup, and SwiftUI async patterns. Start with the free tier to try Swift async code generation.

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free