Basic async/await
```swift
// Sequential async operations
func fetchUserProfile(userId: String) async throws -> UserProfile {
let user = try await api.fetchUser(userId)
let posts = try await api.fetchPosts(userId: userId)
let followers = try await api.fetchFollowers(userId: userId)
return UserProfile(user: user, posts: posts, followers: followers)
}
// Concurrent with async let
func fetchUserProfileConcurrently(userId: String) async throws -> UserProfile {
async let user = api.fetchUser(userId)
async let posts = api.fetchPosts(userId: userId)
async let followers = api.fetchFollowers(userId: userId)
// All three run concurrently, await collects results
return try await UserProfile(user: user, posts: posts, followers: followers)
}
```
Actor for Thread Safety
```swift
actor ImageCache {
private var cache: [URL: UIImage] = [:]
private var inProgress: [URL: Task] = [:]
func image(for url: URL) async throws -> UIImage {
// Return cached
if let cached = cache[url] {
return cached
}
// Return in-progress task (avoid duplicate downloads)
if let existing = inProgress[url] {
return try await existing.value
}
// Start new download
let task = Task {
let (data, _) = try await URLSession.shared.data(from: url)
guard let image = UIImage(data: data) else {
throw ImageError.invalidData
}
return image
}
inProgress[url] = task
do {
let image = try await task.value
cache[url] = image
inProgress[url] = nil
return image
} catch {
inProgress[url] = nil
throw error
}
}
func clearCache() {
cache.removeAll()
}
// Nonisolated for synchronous read
nonisolated var cacheDescription: String {
"ImageCache instance"
}
}
```
TaskGroup for Parallel Operations
```swift
func fetchAllProducts(ids: [String]) async throws -> [Product] {
try await withThrowingTaskGroup(of: Product.self) { group in
for id in ids {
group.addTask {
try await self.api.fetchProduct(id: id)
}
}
var products: [Product] = []
for try await product in group {
products.append(product)
}
return products
}
}
// With concurrency limit
func fetchWithLimit(ids: [String], maxConcurrent: Int = 4) async throws -> [Product] {
try await withThrowingTaskGroup(of: Product.self) { group in
var iterator = ids.makeIterator()
var products: [Product] = []
// Start initial batch
for _ in 0..
if let id = iterator.next() {
group.addTask { try await self.api.fetchProduct(id: id) }
}
}
// As each completes, add another
for try await product in group {
products.append(product)
if let id = iterator.next() {
group.addTask { try await self.api.fetchProduct(id: id) }
}
}
return products
}
}
```
MainActor for UI
```swift
@MainActor
final class ProductListViewModel: ObservableObject {
@Published private(set) var products: [Product] = []
@Published private(set) var isLoading = false
@Published private(set) var error: Error?
private let repository: ProductRepository
init(repository: ProductRepository) {
self.repository = repository
}
func loadProducts() async {
isLoading = true
error = nil
do {
products = try await repository.fetchProducts()
} catch {
self.error = error
}
isLoading = false
}
// Nonisolated for non-UI work
nonisolated func precomputeHash(for product: Product) -> Int {
product.hashValue
}
}
```
Sendable Conformance
```swift
// Value types are Sendable automatically if properties are
struct Product: Sendable {
let id: String
let name: String
let price: Decimal
}
// Classes need explicit conformance
final class ProductCache: @unchecked Sendable {
private let lock = NSLock()
private var cache: [String: Product] = [:]
func get(_ id: String) -> Product? {
lock.lock()
defer { lock.unlock() }
return cache[id]
}
func set(_ product: Product) {
lock.lock()
defer { lock.unlock() }
cache[product.id] = product
}
}
// Sendable closure
func process(_ items: [Item], transform: @Sendable (Item) -> Result) async -> [Result] {
await withTaskGroup(of: Result.self) { group in
for item in items {
group.addTask {
transform(item)
}
}
var results: [Result] = []
for await result in group {
results.append(result)
}
return results
}
}
```
Cancellation Handling
```swift
func downloadLargeFile(url: URL) async throws -> Data {
var data = Data()
let (stream, response) = try await URLSession.shared.bytes(from: url)
let expectedLength = response.expectedContentLength
for try await byte in stream {
// Check for cancellation periodically
try Task.checkCancellation()
data.append(byte)
// Report progress (would need actor for thread safety)
let progress = Double(data.count) / Double(expectedLength)
await reportProgress(progress)
}
return data
}
// Usage with timeout
func downloadWithTimeout(url: URL, timeout: Duration) async throws -> Data {
try await withThrowingTaskGroup(of: Data.self) { group in
group.addTask {
try await self.downloadLargeFile(url: url)
}
group.addTask {
try await Task.sleep(for: timeout)
throw DownloadError.timeout
}
// First to complete wins, other is cancelled
let result = try await group.next()!
group.cancelAll()
return result
}
}
```