Expert Swift concurrency decisions: async let vs TaskGroup selection, actor isolation boundaries, @MainActor placement strategies, Sendable conformance judgment calls, and structured vs unstructured task trade-offs. Use when designing concurrent code, debugging data races, or choosing between concurrency patterns. Trigger keywords: async, await, actor, Task, TaskGroup, @MainActor, Sendable, concurrency, data race, isolation, structured concurrency, continuation
View on GitHubKaakati/rails-enterprise-dev
reactree-ios-dev
January 25, 2026
Select agents to install to:
npx add-skill https://github.com/Kaakati/rails-enterprise-dev/blob/main/plugins/reactree-ios-dev/skills/concurrency-patterns/SKILL.md -a claude-code --skill concurrency-patternsInstallation paths:
.claude/skills/concurrency-patterns/# Concurrency Patterns — Expert Decisions
Expert decision frameworks for Swift concurrency choices. Claude knows async/await syntax — this skill provides judgment calls for pattern selection and isolation boundaries.
---
## Decision Trees
### async let vs TaskGroup
```
Is the number of concurrent operations known at compile time?
├─ YES (2-5 fixed operations)
│ └─ async let
│ async let user = fetchUser()
│ async let posts = fetchPosts()
│ let (user, posts) = await (try user, try posts)
│
└─ NO (dynamic count, array of IDs)
└─ TaskGroup
try await withThrowingTaskGroup(of: User.self) { group in
for id in userIds { group.addTask { ... } }
}
```
**async let gotcha**: All `async let` values MUST be awaited before scope ends. Forgetting to await silently cancels the task — no error, just missing data.
### Task vs Task.detached
```
Does the new task need to inherit context?
├─ YES (inherit priority, actor, task-locals)
│ └─ Task { }
│ Example: Continue work on same actor
│
└─ NO (fully independent execution)
└─ Task.detached { }
Example: Background processing that shouldn't block UI
```
**The trap**: `Task { }` inside `@MainActor` runs on MainActor. For truly background work, use `Task.detached(priority:)`.
### Actor vs Class with Lock
```
Is the mutable state accessed from async contexts?
├─ YES → Actor (compiler-enforced isolation)
│
└─ NO → Is it performance-critical?
├─ YES → Class with lock (less overhead)
│ └─ Consider @unchecked Sendable if crossing boundaries
│
└─ NO → Actor (safer, cleaner)
```
**When actors lose**: High-contention scenarios where lock granularity matters. Actor methods are fully isolated — can't lock just part of the state.
### Sendable Conformance
```
Is the type crossing concurrency boundaries?
├─ NO → Don't add Sendable
│
└─ YES → What kind of type?
├─ Struct with only Sendable properties
│ └─ Implicit Sendable (or add explicit)
│
├─ Class with immutabl