Swift Concurrency
async/await, actors, Sendable e structured concurrency
Swift Concurrency introduz structured concurrency com verificacao em compile-time. O compilador garante que dados compartilhados sao acessados de forma segura, eliminando data races em tempo de compilacao.
GCD vs Structured Concurrency
Swift Concurrency substitui Grand Central Dispatch (GCD) com um modelo que o compilador pode verificar. Tasks formam uma arvore hierarquica β se uma task pai e cancelada, todas as filhas sao canceladas automaticamente.
| Aspecto | GCD (DispatchQueue) | Structured Concurrency |
|---|---|---|
| Modelo | Closures em filas | async/await com Tasks |
| Cancelamento | Manual (flag booleana) | Automatico e hierarquico |
| Erro handling | Callbacks com Result | try/catch nativo |
| Thread safety | Responsabilidade do dev | Compilador verifica via Sendable |
| Prioridade | QoS classes | TaskPriority (herda do pai) |
| Depuracao | Stack frames fragmentados | Stack traces completos |
AsyncStream
AsyncStream permite criar sequencias assincronas que emitem valores ao longo do tempo. Ideal para adaptar APIs callback-based para o mundo async/await.
// AsyncStream β adaptar delegate para async
func frameStream() -> AsyncStream<ProcessedFrame> {
AsyncStream { continuation in
let handler = FrameHandler { frame in
continuation.yield(frame)
}
continuation.onTermination = { _ in
handler.stop()
}
handler.start()
}
}
// Consumir com for-await
func processFrames() async {
for await frame in frameStream() {
guard !Task.isCancelled else { break }
await analyzeFrame(frame)
}
}
CheckedContinuation
CheckedContinuation faz a ponte entre codigo callback-based e async/await. O "checked" significa que o runtime verifica se resume e chamado exatamente uma vez.
// CheckedContinuation β ponte callback -> async
func requestCameraPermission() async -> Bool {
await withCheckedContinuation { continuation in
AVCaptureDevice.requestAccess(for: .video) { granted in
continuation.resume(returning: granted)
// Chamar resume 2x causa crash (checked!)
}
}
}
// Throwing version
func loadImage(at url: URL) async throws -> UIImage {
try await withCheckedThrowingContinuation { continuation in
ImageLoader.load(url: url) { result in
switch result {
case .success(let image):
continuation.resume(returning: image)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}