Section 6/131 menit
6. Continuation
6. Continuation
Teori: Jembatan dari Callback ke Async World
Banyak API Apple dan library pihak ketiga masih menggunakan completion handler.
Continuation memungkinkan kita membungkus API tersebut menjadi async throws function
tanpa mengubah library aslinya.
swift
Kode async (await) ←──── Continuation ────→ Callback API
withCheckedContinuation — Aman dengan Runtime Check
swift
// Membungkus URLSession dataTask (cara lama) menjadi async
func fetchDataLegacy(from url: URL) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
URLSession.shared.dataTask(with: url) { data, response, error in
// Resume HARUS dipanggil tepat satu kali
if let error = error {
continuation.resume(throwing: error)
return
}
guard let data = data else {
continuation.resume(throwing: NetworkError.noData)
return
}
continuation.resume(returning: data)
// Setelah ini jangan panggil continuation lagi!
}.resume()
}
}
// Membungkus operasi yang tidak bisa gagal
func requestPermission() async -> Bool {
await withCheckedContinuation { continuation in
PHPhotoLibrary.requestAuthorization { status in
continuation.resume(returning: status == .authorized)
}
}
}
Aturan Continuation:
resumeHARUS dipanggil tepat 1 kali- Memanggil 0 kali → task leak (hang selamanya)
- Memanggil 2+ kali → crash (
withCheckedContinuationakan crash,withUnsafeContinuationundefined behavior)
AsyncStream dari Delegate
Untuk API yang memanggil callback berulang kali (delegate, NotificationCenter):
swift
// CLLocationManager → AsyncStream
class LocationService {
private let locationManager = CLLocationManager()
// Expose lokasi sebagai AsyncStream
var locationStream: AsyncStream<CLLocation> {
AsyncStream { continuation in
let delegate = LocationDelegate { location in
continuation.yield(location)
}
continuation.onTermination = { _ in
// Cleanup saat stream dihentikan
delegate.stopUpdates()
}
locationManager.delegate = delegate
locationManager.startUpdatingLocation()
}
}
}
// Penggunaan
let locationService = LocationService()
Task {
for await location in locationService.locationStream {
print("Lokasi baru: \(location.coordinate)")
// Stream otomatis berhenti saat Task di-cancel
if shouldStop {
break
}
}
}
swift
// NotificationCenter → AsyncStream
extension NotificationCenter {
func notifications(named name: Notification.Name,
object: AnyObject? = nil) -> AsyncStream<Notification> {
AsyncStream { continuation in
let observer = addObserver(forName: name, object: object, queue: nil) { notification in
continuation.yield(notification)
}
continuation.onTermination = { _ in
self.removeObserver(observer)
}
}
}
}
// Penggunaan
Task {
for await _ in NotificationCenter.default.notifications(named: UIApplication.didBecomeActiveNotification) {
await refreshData()
}
}