Giriş#
Swift 5.5 ile birlikte gelen async/await, iOS geliştirme dünyasında callback ve completion handler cehenneminden kurtulmanın en temiz yolu haline geldi. Bu yazıda gerçek dünya senaryolarıyla Swift Concurrency’yi ele alacağız.
Neden async/await?#
Klasik completion handler yaklaşımının sorunları:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // ❌ Eski yöntem — callback cehennemi
func fetchUser(id: String, completion: @escaping (Result<User, Error>) -> Void) {
networkService.get("/users/\(id)") { result in
switch result {
case .success(let data):
do {
let user = try JSONDecoder().decode(User.self, from: data)
self.fetchPosts(for: user) { postsResult in
// Bir kat daha içeriye...
}
} catch {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
| // ✅ async/await ile temiz kod
func fetchUser(id: String) async throws -> User {
let data = try await networkService.get("/users/\(id)")
return try JSONDecoder().decode(User.self, from: data)
}
func loadUserWithPosts(id: String) async throws -> (User, [Post]) {
let user = try await fetchUser(id: id)
let posts = try await fetchPosts(for: user)
return (user, posts)
}
|
Structured Concurrency ile Paralel İşlemler#
İki bağımsız network isteğini paralel yapmak için async let kullanın:
1
2
3
4
5
6
7
8
9
10
11
12
| func loadDashboard() async throws -> Dashboard {
async let user = fetchUser(id: currentUserID)
async let notifications = fetchNotifications()
async let feed = fetchFeed()
// Üçü paralel çalışır, hepsi bitince devam eder
return Dashboard(
user: try await user,
notifications: try await notifications,
feed: try await feed
)
}
|
TaskGroup ile Dinamik Paralellik#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| func fetchAllPosts(ids: [String]) async throws -> [Post] {
try await withThrowingTaskGroup(of: Post.self) { group in
for id in ids {
group.addTask {
try await self.fetchPost(id: id)
}
}
var posts: [Post] = []
for try await post in group {
posts.append(post)
}
return posts
}
}
|
Actor ile Thread Safety#
Shared mutable state’i güvenli hale getirmek için actor kullanın:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| actor UserCache {
private var cache: [String: User] = [:]
func user(for id: String) -> User? {
cache[id]
}
func store(_ user: User, for id: String) {
cache[id] = user
}
}
// Kullanım
let cache = UserCache()
await cache.store(user, for: user.id)
let cachedUser = await cache.user(for: id)
|
MainActor ile UI Güncellemeleri#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| @MainActor
class ProfileViewModel: ObservableObject {
@Published var user: User?
@Published var isLoading = false
func loadProfile() async {
isLoading = true
defer { isLoading = false }
do {
user = try await userService.fetchCurrentUser()
} catch {
// Hata yönetimi
}
}
}
|
Sonuç#
Swift Concurrency, iOS kodunu hem okunabilir hem de hata yapmaya karşı dayanıklı hale getiriyor. async/await, actor ve TaskGroup üçlüsünü kullanarak modern, güvenli ve performanslı uygulamalar yazabilirsiniz.
Bir sonraki yazıda AsyncStream ve AsyncSequence konularına değineceğiz.