Files
2nd/10_Wiki/Topics/Coding/iOS_Strict_Concurrency.md
T
2026-05-09 21:08:02 +09:00

325 lines
7.1 KiB
Markdown

---
id: ios-strict-concurrency
title: Swift 6 Strict Concurrency — Sendable / Isolation
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [ios, swift, concurrency, vibe-coding]
tech_stack: { language: "Swift 6", applicable_to: ["iOS"] }
applied_in: []
aliases: [Swift 6, strict concurrency, Sendable, isolation, data race, complete checking]
---
# Swift 6 Strict Concurrency
> Swift 6 = data race 컴파일 시 잡음. **`Sendable` 강제, isolation 명시**. 점진 도입 (`@preconcurrency`). 큰 코드는 마이그레이션 작업.
## 📖 핵심 개념
- Strict concurrency: 모든 cross-actor 가 검증.
- Sendable: thread-safe 표시.
- Isolation: 어떤 actor / context.
- @preconcurrency: 옛 코드 호환 가뭄.
## 💻 코드 패턴
### 활성화
```swift
// Package.swift
.target(
name: "MyApp",
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),
]
)
// Xcode Build Settings Strict Concurrency Checking Complete
```
```swift
// Swift 6 mode
swiftLanguageVersions: [.v6]
```
### Sendable 자동
```swift
// Struct field Sendable Sendable
struct User: Sendable {
let id: UUID
let email: String
}
// Class
final class Config: Sendable {
let theme: String
init(theme: String) { self.theme = theme }
}
// Mutable class = Sendable X ()
class MutableState { // actor
var x = 0
}
```
### @MainActor
```swift
@MainActor
class ViewModel: ObservableObject {
@Published var users: [User] = []
func load() async {
let fetched = await api.fetchUsers() // off main
users = fetched // back to main
}
}
```
→ 자동 main thread.
### Actor isolation
```swift
actor BankAccount {
private var balance: Decimal = 0
func deposit(_ amount: Decimal) {
balance += amount
}
var current: Decimal { balance }
}
let acc = BankAccount()
await acc.deposit(100) // await (cross-actor)
let bal = await acc.current
```
### Isolation 명시
```swift
@MainActor
func updateUI() { ... }
@globalActor actor DatabaseActor {
static let shared = DatabaseActor()
}
@DatabaseActor
func saveToDB() { ... }
```
### nonisolated
```swift
actor Logger {
private var logs: [String] = []
func add(_ msg: String) { logs.append(msg) }
nonisolated var name: String { "AppLogger" } // read OK
}
```
### Cross-actor 호출
```swift
@MainActor func ui() {}
actor DB { func write() {} }
let db = DB()
@MainActor
func handle() async {
await db.write() // cross-actor await + Sendable args
}
```
### Sending parameters (Swift 6 의 새 feature)
```swift
// Sendable X OK
actor Worker {
func process(_ data: sending NonSendable) {
// data caller X (move-like)
}
}
```
→ Class 가 mutable 도 한 번만 보내면 OK.
### @preconcurrency (legacy)
```swift
// lib import
import @preconcurrency Foundation
// specific
@preconcurrency import OldLib
```
→ Sendable warning 무시 (옛 lib 가 안 표시).
### Async let + Sendable
```swift
async let user = api.fetchUser(id) // Sendable result
async let orders = api.fetchOrders(uid)
let dashboard = try await Dashboard(user: user, orders: orders)
```
### TaskGroup — Sendable result
```swift
func loadAll() async throws -> [URL: Data] {
try await withThrowingTaskGroup(of: (URL, Data).self) { group in
for url in urls {
group.addTask {
let (data, _) = try await URLSession.shared.data(from: url)
return (url, data) // tuple Sendable
}
}
var result: [URL: Data] = [:]
for try await (url, data) in group {
result[url] = data
}
return result
}
}
```
### Inheritance 와 Sendable
```swift
class Base: Sendable {
let name: String
init(name: String) { self.name = name }
}
final class Sub: Base, @unchecked Sendable { //
// ...
}
// final + Sendable = final .
```
### @unchecked Sendable (lock 직접 관리)
```swift
final class ThreadSafeCache: @unchecked Sendable {
private let lock = NSLock()
private var storage: [String: String] = [:]
func set(_ key: String, _ value: String) {
lock.lock(); defer { lock.unlock() }
storage[key] = value
}
}
```
→ 검사 우회 — 책임 너에게.
### Migration 점진
```swift
// Phase 1: Strict concurrency = Minimal (default)
// Phase 2: Targeted ( module)
// Phase 3: Complete
// Module
.target(
name: "Core",
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
)
```
### Common errors + fix
```swift
// Error: "Sending non-Sendable value"
class MyData { var x = 0 }
Task { await actor.use(MyData()) } //
// Fix 1: struct
struct MyData: Sendable { var x = 0 }
// Fix 2: final class + Sendable
final class MyData: Sendable {
let x: Int
init(x: Int) { self.x = x }
}
// Fix 3: sending (Swift 6)
actor MyActor {
func use(_ data: sending MyData) { ... }
}
```
```swift
// Error: "Cannot use mutable state from another isolation"
@MainActor var counter = 0
Task {
counter += 1 // main actor
}
// Fix
Task { @MainActor in
counter += 1
}
//
await MainActor.run { counter += 1 }
```
### Async sequence isolation
```swift
@MainActor
class Stream {
func observe() async {
for await value in someAsyncStream { // actor yield
// value Sendable
}
}
}
```
### Performance impact
```
거의 없음 — 컴파일 시 검사.
일부 boxing 추가 가능 (cross-actor sending).
```
### Tooling
```
Xcode: warning / error 표시.
swift -strict-concurrency=complete: CLI.
```
### Adoption advice
```
1. 새 프로젝트 = Swift 6 mode + complete.
2. 기존 = minimal → targeted → complete 점진.
3. 한 module 씩 + @preconcurrency 로 의존.
4. Sendable struct 우선 (class 보다).
5. @MainActor / actor isolation 명시.
```
## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 새 프로젝트 | Swift 6 + Complete |
| 기존 큰 codebase | 점진 (minimal → complete) |
| Old library 의존 | @preconcurrency |
| UI / ViewModel | @MainActor |
| Mutable shared state | actor |
| Pure data | struct + Sendable |
## ❌ 안티패턴
- **@unchecked Sendable 남발**: data race 검사 무효.
- **모든 거 @MainActor**: contention.
- **Class 가 모든 곳 — Sendable 어려움**: struct 우선.
- **Cross-actor 무거운 데이터 매번**: copy 비용. id 만 전달.
- **nonisolated + mutable**: race.
- **@preconcurrency 영원 유지**: 점진 마이그.
- **`Task.detached` 무절제**: priority / context 잃음.
## 🤖 LLM 활용 힌트
- Swift 6 = Strict concurrency default.
- Sendable struct + actor + @MainActor 3종.
- @preconcurrency 점진 마이그.
- @unchecked 마지막 카드.
## 🔗 관련 문서
- [[iOS_Swift_Concurrency_Actor_Patterns]]
- [[iOS_Swift_Concurrency_async_await]]
- [[iOS_Swift_Macros]]