Tüm Yazılar
KategoriSwift
Okuma Süresi
19 dk
Yayın Tarihi
...
Kelime Sayısı
1.488kelime

Kahveni hazırla - bu içerikli bir makale!

Swift concurrency'nin doğru kullanımı. Task, TaskGroup, actor, MainActor ve structured concurrency patterns.

Async/Await Best Practices: Swift Concurrency Mastery

Swift'in modern concurrency sistemi, asenkron kod yazmayı güvenli ve okunabilir hale getiriyor.


İçindekiler


Swift Concurrency Yaklasimlari Karsilastirmasi

Yaklasim
Thread Safety
Okunabilirlik
Performans
iOS Minimum
Onerilen Kullanim
**GCD (DispatchQueue)**
Manuel
Dusuk
Iyi
iOS 4+
Legacy projeler
**OperationQueue**
Manuel
Orta
Iyi
iOS 4+
Karmasik is akislari
**Combine**
Otomatik
Orta
Iyi
iOS 13+
Reactive streams
**async/await**
Otomatik
Yuksek
Cok iyi
iOS 13+
Genel asenkron isler
**Actor**
Otomatik
Yuksek
Iyi
iOS 13+
Shared mutable state
**TaskGroup**
Otomatik
Yuksek
Cok iyi
iOS 13+
Paralel islemler
**AsyncSequence**
Otomatik
Yuksek
Iyi
iOS 13+
Streaming veri
Yeni projelerde async/await ve Actor tercih edin. GCD sadece cok dusuk seviye performans gereksinimleri icin kullanilmalidir.

Async/Await Temelleri

swift
1// Async function tanımı
2func fetchUser(id: UUID) async throws -> User {
3 let url = URL(string: "https://api.example.com/users/\(id)")!
4 let (data, response) = try await URLSession.shared.data(from: url)
5
6 guard let httpResponse = response as? HTTPURLResponse,
7 httpResponse.statusCode == 200 else {
8 throw NetworkError.invalidResponse
9 }
10
11 return try JSONDecoder().decode(User.self, from: data)
12}
13 
14// Çağırma
15Task {
16 do {
17 let user = try await fetchUser(id: userId)
18 print("User: \(user.name)")
19 } catch {
20 print("Error: \(error)")
21 }
22}

Task ve Task Groups

swift
1// Serial execution
2func loadUserData(userId: UUID) async throws -> UserProfile {
3 let user = try await fetchUser(id: userId)
4 let posts = try await fetchPosts(for: userId)
5 let followers = try await fetchFollowers(for: userId)
6
7 return UserProfile(user: user, posts: posts, followers: followers)
8}
9 
10// Parallel execution with async let
11func loadUserDataParallel(userId: UUID) async throws -> UserProfile {
12 async let user = fetchUser(id: userId)
13 async let posts = fetchPosts(for: userId)
14 async let followers = fetchFollowers(for: userId)
15
16 return try await UserProfile(
17 user: user,
18 posts: posts,
19 followers: followers
20 )
21}
22 
23// TaskGroup for dynamic parallelism
24func fetchAllUsers(ids: [UUID]) async throws -> [User] {
25 try await withThrowingTaskGroup(of: User.self) { group in
26 for id in ids {
27 group.addTask {
28 try await fetchUser(id: id)
29 }
30 }
31
32 var users: [User] = []
33 for try await user in group {
34 users.append(user)
35 }
36 return users
37 }
38}
39 
40// TaskGroup with rate limiting
41func fetchWithRateLimit(ids: [UUID], maxConcurrent: Int = 5) async throws -> [User] {
42 try await withThrowingTaskGroup(of: User.self) { group in
43 var iterator = ids.makeIterator()
44 var users: [User] = []
45
46 // Start initial batch
47 for _ in 0..<min(maxConcurrent, ids.count) {
48 if let id = iterator.next() {
49 group.addTask { try await fetchUser(id: id) }
50 }
51 }
52
53 // Process results and add new tasks
54 for try await user in group {
55 users.append(user)
56
57 if let id = iterator.next() {
58 group.addTask { try await fetchUser(id: id) }
59 }
60 }
61
62 return users
63 }
64}

Actors ve Data Isolation

swift
1// Actor for thread-safe state
2actor ImageCache {
3 private var cache: [URL: UIImage] = [:]
4 private var inProgress: [URL: Task<UIImage, Error>] = [:]
5
6 func image(for url: URL) async throws -> UIImage {
7 // Return cached image
8 if let cached = cache[url] {
9 return cached
10 }
11
12 // Return in-progress task
13 if let task = inProgress[url] {
14 return try await task.value
15 }
16
17 // Start new download
18 let task = Task {
19 let (data, _) = try await URLSession.shared.data(from: url)
20 guard let image = UIImage(data: data) else {
21 throw ImageError.invalidData
22 }
23 return image
24 }
25
26 inProgress[url] = task
27
28 do {
29 let image = try await task.value
30 cache[url] = image
31 inProgress[url] = nil
32 return image
33 } catch {
34 inProgress[url] = nil
35 throw error
36 }
37 }
38
39 func clearCache() {
40 cache.removeAll()
41 }
42}
43 
44// Global actor
45@globalActor
46actor DatabaseActor {
47 static let shared = DatabaseActor()
48}
49 
50@DatabaseActor
51class DatabaseManager {
52 func save(_ entity: Entity) async {
53 // Database operations
54 }
55}

MainActor ve UI Updates

swift
1@MainActor
2class ProfileViewModel: ObservableObject {
3 @Published private(set) var user: User?
4 @Published private(set) var isLoading = false
5 @Published private(set) var error: Error?
6
7 private let repository: UserRepositoryProtocol
8
9 init(repository: UserRepositoryProtocol) {
10 self.repository = repository
11 }
12
13 func loadUser(id: UUID) async {
14 isLoading = true
15 defer { isLoading = false }
16
17 do {
18 user = try await repository.fetchUser(id: id)
19 } catch {
20 self.error = error
21 }
22 }
23}
24 
25// Explicit MainActor usage
26func updateUI(with data: Data) {
27 Task { @MainActor in
28 label.text = String(data: data, encoding: .utf8)
29 }
30}
31 
32// MainActor.run
33func processAndDisplay() async {
34 let result = await heavyComputation()
35
36 await MainActor.run {
37 displayResult(result)
38 }
39}

Task Cancellation

swift
1class SearchViewModel: ObservableObject {
2 @Published var searchText = ""
3 @Published private(set) var results: [SearchResult] = []
4
5 private var searchTask: Task<Void, Never>?
6
7 func search() {
8 // Cancel previous search
9 searchTask?.cancel()
10
11 searchTask = Task {
12 // Debounce
13 try? await Task.sleep(nanoseconds: 300_000_000)
14
15 // Check cancellation
16 guard !Task.isCancelled else { return }
17
18 do {
19 let results = try await performSearch(query: searchText)
20
21 // Check again before UI update
22 guard !Task.isCancelled else { return }
23
24 await MainActor.run {
25 self.results = results
26 }
27 } catch {
28 if !(error is CancellationError) {
29 print("Search error: \(error)")
30 }
31 }
32 }
33 }
34}
35 
36// Cooperative cancellation in loops
37func processItems(_ items: [Item]) async throws {
38 for item in items {
39 try Task.checkCancellation()
40 await process(item)
41 }
42}

AsyncSequence ve AsyncStream

swift
1// AsyncSequence for paginated data
2struct PaginatedProducts: AsyncSequence {
3 typealias Element = [Product]
4
5 let pageSize: Int
6
7 struct AsyncIterator: AsyncIteratorProtocol {
8 var currentPage = 0
9 let pageSize: Int
10 var hasMore = true
11
12 mutating func next() async throws -> [Product]? {
13 guard hasMore else { return nil }
14
15 let products = try await fetchProducts(page: currentPage, size: pageSize)
16 currentPage += 1
17 hasMore = products.count == pageSize
18
19 return products.isEmpty ? nil : products
20 }
21 }
22
23 func makeAsyncIterator() -> AsyncIterator {
24 AsyncIterator(pageSize: pageSize)
25 }
26}
27 
28// Usage
29for try await page in PaginatedProducts(pageSize: 20) {
30 displayProducts(page)
31}
32 
33// AsyncStream for events
34func locationUpdates() -> AsyncStream<CLLocation> {
35 AsyncStream { continuation in
36 let manager = CLLocationManager()
37 let delegate = LocationDelegate { location in
38 continuation.yield(location)
39 }
40
41 manager.delegate = delegate
42 manager.startUpdatingLocation()
43
44 continuation.onTermination = { _ in
45 manager.stopUpdatingLocation()
46 }
47 }
48}

Error Handling

swift
1// Typed errors (Swift 6.0)
2enum DataError: Error {
3 case notFound
4 case networkError(underlying: Error)
5 case decodingFailed
6}
7 
8func fetchData() async throws(DataError) -> Data {
9 do {
10 let (data, _) = try await URLSession.shared.data(from: url)
11 return data
12 } catch let error as URLError {
13 throw .networkError(underlying: error)
14 } catch {
15 throw .notFound
16 }
17}
18 
19// Result-based approach
20func safeFetch() async -> Result<Data, DataError> {
21 do {
22 let data = try await fetchData()
23 return .success(data)
24 } catch {
25 return .failure(error as! DataError)
26 }
27}

Best Practices

  1. Structured concurrency kullanın: Task grupları ile lifecycle yönetimi
  2. Cancellation'ı handle edin: Her async işlemde cancellation kontrolü
  3. MainActor'ı doğru kullanın: Sadece UI güncellemeleri için
  4. Actor'ları tercih edin: Shared mutable state için
  5. async let ile parallelizm: Bağımsız işlemler için

Sonuç

Swift concurrency, güvenli ve performanslı asenkron kod yazmayı kolaylaştırır. async/await, actors ve structured concurrency patterns ile race condition'lardan arınmış kod yazabilirsiniz.

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?

Okuyucu Ödülü

Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Etiketler

#Swift#Concurrency#Async/Await#iOS#Actors
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz