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. Gunakan struct + let sebagai default — otomatis Sendable tanpa 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.