logo
hero image

Swift Continuations

Mengubah Completion Handler menjadi async/await pada Swift
24 March 2023 · 4 Minutes

Completion Handler

Sebelum adanya async/await pada Swift, kita biasanya menggunakan Completion Handler untuk menjalankan sebuah code setelah suatu task selesai dijalankan.

Completion Handler
icon
1func fetchProducts(completion: @escaping ([String]) -> Void) {
2 let url = URL(string: "https://website.com/api/products")!
3
4 URLSession.shared.dataTask(with: url) { data, response, error in
5 if let data = data {
6 if let products = try? JSONDecoder().decode([String].self, from: data) {
7 completion(products)
8 return
9 }
10 }
11
12 completion([])
13 }.resume()
14}
15
16fetchProducts { products in
17 print(products)
18}

Namun, Completion Handler memiliki kekurangan yaitu ketika kita menggunakan nested Completion Handler, code kita akan sulit untuk dibaca karena indentasi code-nya akan semakin dalam, kondisi ini sering disebut sebagai Callback Hell.

Callback HellCallback Hell

Tapi untungnya Swift memiliki fitur untuk mengubah function yang menggunakan Completion Handler agar menjadi async/await tanpa perlu memodifikasi code asli dari function tersebut dan tanpa perlu rewrite code yang sudah ada. Sehingga kita bisa gunakan fitur tersebut untuk code yang bukan milik kita, dari framework lain misalnya.

Jadi konsepnya, kita akan buat satu function baru untuk membungkus function yang menggunakan Completion Handler tersebut.

Kita akan bungkus menggunakan beberapa functions berikut:

  • withCheckedContinuation

  • withCheckedThrowingContinuation

  • withUnsafeContinuation

  • withUnsafeThrowingContinuation


withCheckedContinuation

Berikut adalah contoh penggunaan withCheckedContinuation:

withCheckedContinuation
icon
1func fetchProducts() async -> [String] {
2 let url = URL(string: "https://website.com/api/products")!
3
5 return await withCheckedContinuation { continuation in
6 URLSession.shared.dataTask(with: url) { data, response, error in
7 if let data = data {
8 if let products = try? JSONDecoder().decode([String].self, from: data) {
10 continuation.resume(returning: products)
11 return
12 }
13 }
14
16 continuation.resume(returning: [])
17 }.resume()
18 }
19}
20
21let products = await fetchProducts()

Kita gunakan method continuation.resume(returning:) untuk me-return value.

Ada 2 hal yang harus diperhatikan ketika menggunakan Continuation:

  1. Kita hanya boleh menjalankan method continuation.resume satu kali saja. Jika tidak, maka akan muncul error seperti ini:

1_Concurrency/CheckedContinuation.swift:164: Fatal error: SWIFT TASK CONTINUATION MISUSE: fetchProducts() tried to resume its continuation more than once, returning []!
  1. Kita harus memanggil method continuation.resume. Jika tidak, maka akan muncul error seperti ini:

1SWIFT TASK CONTINUATION MISUSE: fetchProducts() leaked its continuation!

withCheckedThrowingContinuation

withCheckedThrowingContinuation itu sama seperti function sebelumnya, tapi dia bisa throwing error menggunakan method continuation.resume(throwing:)

Berikut adalah contoh penggunaan withCheckedThrowingContinuation:

withCheckedThrowingContinuation
icon
1enum ErrorType: Error {
2 case networkError
3}
4
5func fetchProducts() async throws -> [String] {
6 let url = URL(string: "https://website.com/api/products")!
7
8 return try await withCheckedThrowingContinuation { continuation in
9 URLSession.shared.dataTask(with: url) { data, response, error in
10 if let data = data {
11 if let products = try? JSONDecoder().decode([String].self, from: data) {
12 continuation.resume(returning: products)
13 return
14 }
15 }
16
18 continuation.resume(throwing: ErrorType.networkError)
19 }.resume()
20 }
21}
22
23do {
24 let products = try await fetchProducts()
25 print(products)
26} catch {
27 print(error)
28}

withUnsafeContinuation & withUnsafeThrowingContinuation

Ada 2 tipe Swift Continuations, yaitu Checked (yang telah kita bahas sebelumnya) dan Unsafe.

Bedanya, yang Unsafe itu tidak ada pengecekan-pengecekan pada saat runtime, seperti pengecekan harus memanggil method continuation.resume, dll. sehingga jika ada masalah tidak akan terdeteksi lebih awal dan kita tidak mendapatkan informasi yang jelas pada Crash Log.

Benefit ketika kita menggunakan Unsafe adalah performa aplikasi kita menjadi sedikit lebih cepat, karena tidak ada pengecekan-pengecekan tersebut pada saat runtime.

Checked vs Unsafe

Apple merekomendasikan kita untuk menggunakan tipe Checked pada saat proses development. Lalu setelah kita berhasil menjalankan code kita tanpa muncul error atau warning, dan setelah kita yakin bahwa code kita sudah benar, maka kita bisa mengubahnya menjadi Unsafe.

Kalau saya sendiri lebih memilih untuk selalu menggunakan Checked, bahkan ketika saya sudah yakin bahwa tidak akan muncul runtime error pada saat pengecekan. Selain karena lebih aman, juga agak repot kalau harus mengganti Checked menjadi Unsafe secara manual dan mengembalikan menjadi Checked lagi apabila saya melakukan perubahan pada code tersebut. 😅

Making your code less safe based on assumptions is never a great idea.
Donny Wals

Oke mungkin itu saja yang bisa saya bagikan kali ini, kalau kamu merasa artikel ini bermanfaat silakan Like & Share artikel ini ke teman-teman kamu atau jika kamu punya pertanyaan, tulis aja di kolom komentar, Thank you! 😁 🙏

iOS Development
Swift
Concurrency

Written by :
Alfin Syahruddin
Developer · Stock Trader · Libertarian · Freethinker

Always open to new ideas 🕊️

Loading...

Related articles

Articles that you might want to read.

hero image
Testable Code

Menulis kode yang mudah untuk di-test pada Swift.

1 May 2023 · 8 Minutes
iOS Development
Swift
Testing
hero image
iOS Reverse Engineering

Tutorial membongkar dan memodifikasi aplikasi iOS 🍎

15 March 2025 · 10 Minutes
iOS Development
Security
Reverse Engineering
hero image
CI/CD aplikasi iOS dengan Fastlane dan Github Actions

Otomatisasi build, testing, screenshot, dan deployment aplikasi iOS ke Testflight & AppStore.

24 September 2023 · 9 Minutes
iOS Development
CI/CD
Fastlane
Github Actions
All rights reserved © Alfin Syahruddin · 2019
RSS Feed