Section 9/171 menit
9. Best Practice: Error Handling & Cancellation
9. Best Practice: Error Handling & Cancellation
BP 9.1: Bedakan CancellationError dengan Error Lain
Cancellation bukan error production. Jangan log atau alert.
swift
// ✓ Pisahkan handling
do {
let data = try await fetchData()
self.data = data
} catch is CancellationError {
// diam — ini bagian normal lifecycle
} catch let error as NetworkError {
self.errorAlert = error.userFacingMessage
} catch {
self.errorAlert = "Terjadi kesalahan tak terduga"
logger.error("Unexpected: \(error)")
}
BP 9.2: Convert Completion Handler dengan withCheckedContinuation
Untuk bridging dengan API callback lama, gunakan withCheckedContinuation (atau withCheckedThrowingContinuation). Selalu pastikan continuation di-resume persis sekali.
swift
// ✓ Bridging callback API ke async
func loadLegacy() async throws -> Data {
try await withCheckedThrowingContinuation { cont in
legacyAPI.load { result in
switch result {
case .success(let data): cont.resume(returning: data)
case .failure(let err): cont.resume(throwing: err)
}
}
}
}
Hati-hati: API legacy yang tidak guaranteed memanggil callback (atau memanggil dua kali) akan menyebabkan crash. Pakai withCheckedContinuation (bukan withUnsafeContinuation) untuk diagnostic runtime.
BP 9.3: Typed Throws untuk API yang Stabil
Untuk function dengan error space yang kecil dan stable, pakai typed throws (Swift 6).
swift
enum ParseError: Error {
case malformed
case unsupportedVersion
}
func parse(_ data: Data) throws(ParseError) -> Config {
// ...
}
Caller jadi tahu persis error apa yang mungkin, tanpa harus catch let error as ParseError.
BP 9.4: Cancellation Propagate di Custom AsyncSequence
Saat membuat AsyncSequence kustom, pastikan iterator respect cancellation.
swift
struct PolledSequence: AsyncSequence, Sendable {
typealias Element = Int
let interval: Duration
let max: Int
func makeAsyncIterator() -> Iterator {
Iterator(interval: interval, max: max, count: 0)
}
struct Iterator: AsyncIteratorProtocol {
let interval: Duration
let max: Int
var count: Int
mutating func next() async throws -> Int? {
if count >= max { return nil }
try await Task.sleep(for: interval) // sleep respect cancellation
count += 1
return count
}
}
}