Concurrency in Swift
Introduction
Swift's modern concurrency model provides powerful tools for writing asynchronous code that is both safe and efficient. This guide covers async/await syntax, actors, and structured concurrency.
Async/Await
Basic Syntax
func fetchUserData() async throws -> User {
let data = try await URLSession.shared.data(from: userURL)
return try JSONDecoder().decode(User.self, from: data)
}
// Using async function
async {
do {
let user = try await fetchUserData()
print(user.name)
} catch {
print("Error: \(error)")
}
}
Task Management
// Creating a task
let task = Task {
await performLongOperation()
}
// Task groups
await withTaskGroup(of: Result.self) { group in
for id in ids {
group.addTask {
await processItem(id)
}
}
}
Actors
Definition and Usage
actor BankAccount {
private var balance: Decimal
init(initialBalance: Decimal) {
self.balance = initialBalance
}
func deposit(_ amount: Decimal) {
balance += amount
}
func withdraw(_ amount: Decimal) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
}
// Using an actor
let account = BankAccount(initialBalance: 1000)
await account.deposit(500)
try await account.withdraw(200)
Structured Concurrency
Task Trees
func processImages(_ urls: [URL]) async throws -> [ProcessedImage] {
try await withThrowingTaskGroup(of: ProcessedImage.self) { group in
for url in urls {
group.addTask {
let data = try await downloadImage(from: url)
return try await processImage(data)
}
}
var results: [ProcessedImage] = []
for try await image in group {
results.append(image)
}
return results
}
}
Best Practices
- Use structured concurrency for better code organization
- Implement proper error handling in async contexts
- Avoid data races with actors
- Consider task priorities and cancellation
- Profile concurrent code for performance
Common Patterns
Async Sequences
for await element in asyncSequence {
process(element)
}
// Custom async sequence
struct NumberGenerator: AsyncSequence {
typealias Element = Int
struct AsyncIterator: AsyncIteratorProtocol {
var current = 0
mutating func next() async -> Int? {
guard current < 10 else { return nil }
current += 1
return current
}
}
func makeAsyncIterator() -> AsyncIterator {
AsyncIterator()
}
}
Next Steps
- Learn about Package Management
- Study Performance Optimization
- Explore Security