# Swift Concurrency

> When to activate: async/await, actors, tasks, structured concurrency, MainActor, async sequences in Swift

- Skill: `mattakushi432/swift-concurrency` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/swift-concurrency`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/swift-concurrency/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/swift-concurrency

---


# Swift Concurrency

## async/await Basics

```swift
// Mark async functions with async; call them with await
func loadUser(id: UUID) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// Call from sync context via Task
Task {
    do {
        let user = try await loadUser(id: currentID)
        await MainActor.run { self.user = user }
    } catch {
        await MainActor.run { self.errorMessage = error.localizedDescription }
    }
}
```

## Structured Concurrency with TaskGroup

```swift
func loadFeed(userIDs: [UUID]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        for id in userIDs {
            group.addTask { try await loadUser(id: id) }
        }
        var users: [User] = []
        for try await user in group {
            users.append(user)
        }
        return users
    }
}
```

## async let — Parallel Independent Operations

```swift
func loadProfile(id: UUID) async throws -> Profile {
    async let user    = loadUser(id: id)
    async let posts   = loadPosts(userID: id)
    async let friends = loadFriends(userID: id)

    // All three start concurrently; await collects results
    return Profile(
        user:    try await user,
        posts:   try await posts,
        friends: try await friends
    )
}
```

## Actors

Actors protect mutable state from concurrent access.

```swift
actor Cache<Key: Hashable, Value> {
    private var store: [Key: Value] = [:]

    func get(_ key: Key) -> Value? { store[key] }

    func set(_ key: Key, value: Value) { store[key] = value }

    func remove(_ key: Key) { store.removeValue(forKey: key) }
}

// Accessing actor state requires await
let cache = Cache<String, Data>()
await cache.set("key", value: data)
let cached = await cache.get("key")
```

## @MainActor

Annotate types or methods that must run on the main thread.

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

    func load() async {
        isLoading = true
        defer { isLoading = false }
        items = try! await fetchItems()  // already on MainActor
    }
}

// Or annotate individual methods
class DataService {
    @MainActor func updateUI(with data: [Item]) {
        // safe to touch UIKit/SwiftUI state here
    }
}
```

## AsyncSequence

```swift
// Consuming an AsyncSequence
for await line in url.lines {  // URLSession.lines is an AsyncSequence
    processLine(line)
}

// Custom AsyncSequence via AsyncStream
func timerStream(interval: Duration) -> AsyncStream<Date> {
    AsyncStream { continuation in
        let timer = Timer.scheduledTimer(withTimeInterval: interval.timeInterval, repeats: true) { _ in
            continuation.yield(Date())
        }
        continuation.onTermination = { _ in timer.invalidate() }
    }
}

for await tick in timerStream(interval: .seconds(1)) {
    print("Tick: \(tick)")
}
```

## Task Cancellation

```swift
func fetchWithCancellation(url: URL) async throws -> Data {
    try Task.checkCancellation()  // throws CancellationError if cancelled

    let (data, _) = try await URLSession.shared.data(from: url)

    try Task.checkCancellation()  // check again after suspension
    return data
}

// Cancel from the outside
let task = Task { try await fetchWithCancellation(url: url) }
task.cancel()  // cooperative cancellation
```

## Sendable

```swift
// Value types are Sendable by default (struct, enum with Sendable storage)
struct Message: Sendable {
    let id: UUID
    let text: String
}

// Classes need explicit conformance or @unchecked
final class Config: @unchecked Sendable {
    private let lock = NSLock()
    private var _value: Int = 0
    var value: Int {
        get { lock.withLock { _value } }
        set { lock.withLock { _value = newValue } }
    }
}
```

## Common Anti-Patterns

- **`DispatchQueue.main.async` in async code** — use `await MainActor.run { }` or `@MainActor`
- **Unstructured tasks everywhere** — prefer `async let` and `TaskGroup` for structured concurrency
- **Not checking cancellation** — call `Task.checkCancellation()` at suspension points
- **Calling actor methods with `DispatchQueue`** — actors replace queues; don't mix
- **`@unchecked Sendable` without proper locking** — audit every instance

