Section 12/161 menit

12. Migration Guide — Swift 5 ke Swift 6 Strict Concurrency

12. Migration Guide — Swift 5 ke Swift 6 Strict Concurrency

Fase 1: Audit dan Inventory

swift
# Aktifkan warnings dulu, bukan errors
# -strict-concurrency=targeted: periksa annotation yang sudah ada
swift build -Xswiftc -strict-concurrency=targeted 2>&1 | grep "warning:" | wc -l

# -strict-concurrency=complete: semua pelanggaran
swift build -Xswiftc -strict-concurrency=complete 2>&1 | tee concurrency_warnings.txt

Fase 2: Annotasi @MainActor

swift
// Langkah pertama yang aman: annotasi semua UIViewController, SwiftUI View
// dan ViewModel dengan @MainActor

// Sebelum:
class ProfileViewController: UIViewController {
    var user: User?
    
    func updateUI(with user: User) {
        self.user = user
        // update UI...
    }
}

// Sesudah:
@MainActor
class ProfileViewController: UIViewController {
    var user: User?
    
    func updateUI(with user: User) {
        self.user = user
        // update UI... — sekarang compiler verifikasi ini di main thread
    }
}

Fase 3: Konversi ke Actor

swift
// Sebelum: class dengan manual locking
class UserRepository {
    private var cache: [UserID: User] = [:]
    private let lock = NSLock()
    
    func user(for id: UserID) -> User? {
        lock.withLock { cache[id] }
    }
    
    func store(_ user: User) {
        lock.withLock { cache[user.id] = user }
    }
}

// Sesudah: actor — compiler enforce isolation
actor UserRepository {
    private var cache: [UserID: User] = [:]
    
    func user(for id: UserID) -> User? {
        cache[id]
    }
    
    func store(_ user: User) {
        cache[user.id] = user
    }
}

Fase 4: Sendable Conformance

swift
// Identifikasi types yang perlu Sendable

// Structs: biasanya otomatis, tambahkan explicit untuk clarity
struct UserDTO: Sendable {
    let id: String
    let name: String
    let email: String
}

// Classes: butuh pertimbangan lebih
// Option A: final class dengan internal sync
final class Configuration: Sendable {
    // Semua stored properties harus immutable atau Sendable
    let apiKey: String
    let baseURL: URL
    let timeout: TimeInterval
    
    init(apiKey: String, baseURL: URL, timeout: TimeInterval) {
        self.apiKey = apiKey
        self.baseURL = baseURL
        self.timeout = timeout
    }
}

// Option B: @unchecked Sendable untuk complex cases
final class EventBus: @unchecked Sendable {
    private let lock = NSLock()
    private var subscribers: [EventType: [(Event) -> Void]] = [:]
    
    func subscribe(to eventType: EventType, handler: @escaping (Event) -> Void) {
        lock.withLock { subscribers[eventType, default: []].append(handler) }
    }
    
    func publish(_ event: Event) {
        let handlers = lock.withLock { subscribers[event.type] ?? [] }
        handlers.forEach { $0(event) }
    }
}

Fase 5: @preconcurrency untuk Gradual Adoption

swift
// Untuk module yang belum bisa diupdate sekarang
@preconcurrency
public protocol LegacyDelegate: AnyObject {
    func didReceiveData(_ data: Data)
}

// Import module yang belum Swift 6-ready
@preconcurrency import ThirdPartySDK

// Konform ke protocol yang belum Sendable dengan @preconcurrency
class MyDelegate: @preconcurrency LegacyDelegate {
    func didReceiveData(_ data: Data) {
        // Implementasi
    }
}