swift-concurrency

fusengine's avatarfrom fusengine

Implement Swift 6 concurrency with async/await, actors, Sendable, Task, MainActor. Use when handling asynchronous operations, managing shared state safely, fixing data race errors, or migrating to Swift 6 strict concurrency.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 10, 2026⬇️3downloads

When & Why to Use This Skill

This Claude skill streamlines the implementation of Swift 6's structured concurrency model. It helps developers write safe, high-performance asynchronous code using async/await, actors, and the Sendable protocol, while ensuring compliance with strict concurrency checks to eliminate data races in iOS and macOS applications.

Use Cases

  • Refactoring legacy completion handlers and GCD-based asynchronous code into modern, readable async/await syntax.
  • Eliminating data races and thread-safety issues by implementing Actors for shared state management.
  • Migrating existing projects to Swift 6 strict concurrency by resolving Sendable conformance warnings and compiler errors.
  • Ensuring thread-safe UI updates by correctly applying @MainActor isolation to ViewModels and UI-related functions.
  • Optimizing parallel execution of multiple network requests or heavy computations using Task Groups and async let.
nameswift-concurrency
descriptionImplement Swift 6 concurrency with async/await, actors, Sendable, Task, MainActor. Use when handling asynchronous operations, managing shared state safely, fixing data race errors, or migrating to Swift 6 strict concurrency.

Swift 6 Concurrency

Async/Await Basics

// Async function
func fetchUser(id: String) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// Parallel execution
async let users = fetchUsers()
async let posts = fetchPosts()
let (loadedUsers, loadedPosts) = try await (users, posts)

// Task with cancellation
func loadData() {
    task?.cancel()
    task = Task {
        try Task.checkCancellation()
        let data = try await api.fetch()
        guard !Task.isCancelled else { return }
        await MainActor.run { self.items = data }
    }
}

Actors for Thread Safety

actor DataCache {
    private var storage: [String: Data] = [:]

    func get(_ key: String) -> Data? { storage[key] }
    func set(_ key: String, data: Data) { storage[key] = data }
    func clear() { storage.removeAll() }
}

// Usage - requires await
let cache = DataCache()
await cache.set("user", data: userData)
let data = await cache.get("user")

@MainActor for UI

@MainActor
final class ViewModel: ObservableObject {
    @Published var items: [Item] = []
    @Published var isLoading = false

    func refresh() async {
        isLoading = true
        defer { isLoading = false }
        items = await repository.fetchItems()
    }
}

// Or mark specific function
func updateUI() async {
    await MainActor.run {
        self.label.text = "Updated"
    }
}

Sendable Protocol

// Value types - automatic
struct User: Sendable {
    let id: UUID
    let name: String
}

// Reference types - must be immutable + final
final class Config: Sendable {
    let apiKey: String
    init(apiKey: String) { self.apiKey = apiKey }
}

// @unchecked when you manage safety manually
final class ThreadSafeCache: @unchecked Sendable {
    private let lock = NSLock()
    private var storage: [String: Any] = [:]
}

// Closures
func process(completion: @Sendable @escaping () -> Void) {
    Task { completion() }
}

Common Fixes for Swift 6

Error Solution
"not Sendable" Make struct Sendable or use actor
"actor-isolated" Add await or use nonisolated
"main actor-isolated" Use @MainActor or MainActor.run
"capture of mutable" Use let or capture in Task

Task Groups

func fetchAllUsers(ids: [String]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        for id in ids {
            group.addTask { try await fetchUser(id: id) }
        }
        return try await group.reduce(into: []) { $0.append($1) }
    }
}