What are concurrency features in Swift? How to use async/await and Actor?
Swift 5.5 introduced modern concurrency programming model, including async/await, structured concurrency, Actor and other features, making concurrent programming safer and easier to use.
Basic Usage of async/await:
swiftfunc fetchImage(from urlString: String) async throws -> UIImage { guard let url = URL(string: urlString) else { throw URLError(.badURL) } let (data, _) = try await URLSession.shared.data(from: url) guard let image = UIImage(data: data) else { throw URLError(.cannotDecodeRawData) } return image } Task { do { let image = try await fetchImage(from: "https://example.com/image.jpg") print("Image loaded: \(image)") } catch { print("Error: \(error)") } }
async let Concurrent Execution:
swiftfunc fetchUserData() async throws -> User { async let profile = fetchProfile() async let posts = fetchPosts() async let friends = fetchFriends() let (userProfile, userPosts, userFriends) = try await (profile, posts, friends) return User(profile: userProfile, posts: userPosts, friends: userFriends) }
TaskGroup:
swiftfunc downloadImages(urls: [URL]) async throws -> [UIImage] { try await withThrowingTaskGroup(of: UIImage.self) { group in var images: [UIImage] = [] for url in urls { group.addTask { try await downloadImage(from: url) } } for try await image in group { images.append(image) } return images } }
Actor:
- Ensures thread-safe data access
- Prevents data races
- Example:
swift
actor Counter { private var value = 0 func increment() { value += 1 } func getValue() -> Int { return value } } let counter = Counter() await counter.increment() let count = await counter.getValue()
MainActor:
- Ensures code executes on main thread
- Used for UI updates
- Example:
swift
@MainActor class ViewModel: ObservableObject { @Published var isLoading = false func loadData() async { isLoading = true let data = try? await fetchData() isLoading = false } }
Task:
- Creates asynchronous tasks
- Can be cancelled
- Example:
swift
let task = Task { for i in 1...10 { print(i) try? await Task.sleep(nanoseconds: 1_000_000_000) } } // Cancel task task.cancel()
Continuation:
- Converts callback-based APIs to async/await
- Example:
swift
func fetchImageContinuation(from urlString: String) async throws -> UIImage { try await withCheckedThrowingContinuation { continuation in fetchImageCallback(from: urlString) { result in switch result { case .success(let image): continuation.resume(returning: image) case .failure(let error): continuation.resume(throwing: error) } } } }
Best Practices for Concurrent Programming:
- Use async/await instead of closure callbacks
- Use Actor to protect shared state
- Use MainActor to update UI
- Use TaskGroup to handle concurrent tasks
- Properly handle task cancellation