Section 10/131 menit

10. Troubleshooting & Common Mistakes

10. Troubleshooting & Common Mistakes

Kesalahan 1: Sendable Violation

swift
// ERROR: 'NonSendableType' cannot be sent to actor-isolated method
class Config {  // class biasa, tidak Sendable
    var timeout: Int = 30
}

actor Service {
    func configure(with config: Config) { }  // ERROR di Swift 6
}

// Solusi A: jadikan struct (paling direkomendasikan)
struct Config: Sendable {
    var timeout: Int = 30
}

// Solusi B: jadikan final + immutable + Sendable
final class Config: Sendable {
    let timeout: Int
    init(timeout: Int) { self.timeout = timeout }
}

// Solusi C: pindahkan logika ke dalam actor
actor Service {
    var timeout: Int = 30
    
    func setTimeout(_ value: Int) {
        timeout = value
    }
}

Kesalahan 2: Actor Reentrancy Bug

swift
actor Inventory {
    var stock: [String: Int] = ["item_A": 5]
    
    // BUGGY: reentrancy bisa menyebabkan overselling
    func purchase(item: String) async -> Bool {
        guard let count = stock[item], count > 0 else { return false }
        await processPayment()  // suspension point!
        stock[item]! -= 1      // stock mungkin sudah 0 saat ini
        return true
    }
    
    // BENAR: modifikasi state sebelum suspension
    func purchaseSafe(item: String) async -> Bool {
        guard let count = stock[item], count > 0 else { return false }
        stock[item]! -= 1      // kurangi dulu sebelum await
        await processPayment()
        return true
    }
    
    private func processPayment() async {
        try? await Task.sleep(for: .milliseconds(100))
    }
}

Kesalahan 3: Deadlock dengan Actor + DispatchQueue

swift
// JANGAN lakukan ini — bisa deadlock
actor MyActor {
    func doWork() {
        DispatchQueue.main.sync {  // ❌ sync dari actor bisa deadlock!
            // update UI
        }
    }
    
    // BENAR: gunakan await MainActor.run
    func doWorkCorrectly() async {
        await MainActor.run {
            // update UI — aman
        }
    }
}

Kesalahan 4: Task.detached Tanpa Perlu

swift
@MainActor
class VC: UIViewController {
    func loadData() {
        // HINDARI:
        Task.detached {
            let data = await fetchData()
            await MainActor.run { self.display(data) }
        }
        
        // LEBIH BAIK:
        Task {
            let data = await fetchData()
            display(data)  // otomatis di @MainActor
        }
    }
}

Kesalahan 5: Mengabaikan Task Cancellation

swift
actor DataFetcher {
    func fetchLargeDataset() async throws -> [String] {
        var results: [String] = []
        
        for i in 0..<1000 {
            // SELALU cek cancellation di loop panjang
            try Task.checkCancellation()
            
            let item = await fetchItem(at: i)
            results.append(item)
        }
        
        return results
    }
    
    private func fetchItem(at index: Int) async -> String {
        try? await Task.sleep(for: .milliseconds(10))
        return "item_\(index)"
    }
}