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
}
}