Section 13/132 menit
13. Summary
13. Summary
Gambaran Besar
Swift Concurrency di Swift 5.x bukan fitur tunggal — melainkan sekumpulan tool yang bekerja bersama untuk menggantikan pola lama (GCD, callback, delegate) dengan model yang lebih aman dan mudah dibaca. Kuncinya adalah memahami siapa yang memiliki data dan di thread mana sesuatu boleh berjalan.
swift
Masalah Lama Solusi Swift Concurrency
─────────────────────────────────────────────────────────
Callback hell → async/await
DispatchGroup → async let / withTaskGroup
DispatchQueue.main.async → @MainActor
Manual lock (NSLock, dll.) → actor
Delegate berulang → AsyncStream
Callback satu kali → withCheckedContinuation
Data race pada class → Sendable / actor
Library lama → warning → @preconcurrency
Hierarki Isolation di VIP + UIKit
swift
┌─────────────────────────────────────────────────────┐
│ @MainActor │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ViewController │ │ Presenter │ │
│ │ - trigger Task │ │ - format → ViewModel │ │
│ │ - update UI │ │ - panggil ViewController │ │
│ └────────┬────────┘ └──────────────────────────┘ │
└───────────┼─────────────────────────────────────────┘
│ await interactor.fetch()
┌───────────▼─────────────────────────────────────────┐
│ actor (ProfileInteractor) │
│ - orchestrate business logic │
│ - await worker (async) │
│ - await presenter (auto-hop ke @MainActor) │
└───────────┬─────────────────────────────────────────┘
│ try await worker.fetch()
┌───────────▼─────────────────────────────────────────┐
│ async func (ProfileWorker) │
│ - stateless, pure async │
│ - network / cache / database │
└─────────────────────────────────────────────────────┘
│
┌───────────▼─────────────────────────────────────────┐
│ Sendable Models │
│ struct Request / Response / ViewModel │
│ - value type, aman lintas batas actor │
└─────────────────────────────────────────────────────┘
Aturan Sederhana Per Layer
| Layer | Tipe | Isolation | Alasan |
|---|---|---|---|
| ViewController | class |
@MainActor |
UIKit hanya boleh disentuh di main thread |
| Presenter | class |
@MainActor |
Memanggil ViewController yang @MainActor |
| Router | class |
@MainActor |
Navigasi UIKit butuh main thread |
| Interactor | actor |
actor isolation | Menjaga state business logic dari data race |
| Worker | struct |
none (async) | Stateless, tidak perlu isolation |
| Models | struct |
Sendable |
Dikirim lintas actor, harus value type |
Konsekuensi Tidak Conform Sendable
| Tipe | Tanpa Sendable di Swift 5.x | Tanpa Sendable di Swift 6 |
|---|---|---|
struct |
⚠️ Warning, tetap jalan | ❌ Compile error |
class |
⚠️ Warning + risiko data race di runtime | ❌ Compile error |
enum |
⚠️ Warning, tetap jalan | ❌ Compile error |
Prinsip: Model yang melewati batas actor harus
Sendable. Gunakanstruct+letsebagai default — otomatisSendabletanpa deklarasi eksplisit.
Pola Pemanggilan yang Benar
swift
// ✅ ViewController (@MainActor) → Interactor (actor)
Task {
await interactor.fetchProfile(request: request)
}
// ✅ Interactor (actor) → Worker (async func)
let user = try await worker.fetchUser(id: id)
// ✅ Interactor (actor) → Presenter (@MainActor)
// Swift otomatis hop ke MainActor karena protocol adalah @MainActor
await presenter?.presentProfile(response: response)
// ✅ Protocol dengan @MainActor di level protocol — bukan per method
@MainActor
protocol ProfilePresentationLogic: AnyObject {
func presentProfile(response: ProfileResponse)
func presentError(error: Error)
}
// ✅ Model yang dikirim lintas actor
struct ProfileResponse: Sendable {
let user: User // User: Sendable
let posts: [Post] // Post: Sendable
}
Pola yang Harus Dihindari
swift
// ❌ Campur GCD dan Swift Concurrency di layer yang sama
func load() async {
let data = await fetch()
DispatchQueue.main.async { self.label.text = data } // tidak perlu, sudah @MainActor
}
// ❌ Task.detached tanpa alasan jelas
Task.detached {
await self.update() // kehilangan isolation context
}
// ❌ @unchecked Sendable sebagai jalan pintas
final class MyModel: @unchecked Sendable {
var items: [String] = [] // tidak ada proteksi → data race
}
// ❌ @MainActor per-method di protocol — redundan
protocol MyProtocol: AnyObject {
@MainActor func methodA() // cukup taruh @MainActor di protocol-nya
@MainActor func methodB()
}
// ❌ Block main thread
let result = Task { await service.fetch() }.value // deadlock jika dipanggil dari @MainActor
Jalur Migrasi Bertahap
swift
Swift 5.x (sekarang)
│
▼
1. Aktifkan -warn-concurrency
│
▼
2. Migrasi Worker/Service → async throws + Sendable model
│
▼
3. Migrasi Interactor → actor
│
▼
4. Migrasi ViewController/Presenter → @MainActor
│
▼
5. -strict-concurrency=complete (semua warning tampil)
│
▼
6. Swift 6 Mode (warning → error, compiler menjamin segalanya)
Tidak ada kewajiban sampai ke Step 6 — project yang berhenti di Step 4 sudah jauh lebih aman dari kode GCD tradisional.