Section 2/131 menit
2. async/await
2. async/await
Teori: Masalah yang Dipecahkan
Sebelum async/await, kode asinkron di Swift menggunakan completion handler:
swift
// SEBELUM — Completion handler: callback pyramid, error prone
func loadUserProfile(id: String,
completion: @escaping (Result<User, Error>) -> Void) {
URLSession.shared.dataTask(with: makeRequest(id: id)) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(AppError.noData))
return
}
do {
let user = try JSONDecoder().decode(User.self, from: data)
completion(.success(user))
} catch {
completion(.failure(error))
}
}.resume()
}
// Pemanggil harus ingat memanggil completion di semua code path
loadUserProfile(id: "123") { result in
switch result {
case .success(let user):
DispatchQueue.main.async { self.updateUI(user: user) }
case .failure(let error):
DispatchQueue.main.async { self.showError(error) }
}
}
Masalah utama:
- Mudah lupa memanggil
completion→ silent hang - Harus manual dispatch ke main thread untuk UI
- Sulit dibaca karena alur tidak linear
- Error handling terpencar
Sintaks async/await
swift
// SESUDAH — async/await: linear, mudah dibaca
func loadUserProfile(id: String) async throws -> User {
let request = makeRequest(id: id)
// await menangguhkan fungsi ini, thread tidak diblokir
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(User.self, from: data)
}
// Cara memanggil dari konteks non-async (misal: UIKit action)
Task {
do {
let user = try await loadUserProfile(id: "123")
await MainActor.run { self.updateUI(user: user) }
} catch {
await MainActor.run { self.showError(error) }
}
}
Keyword penting:
async— tandai fungsi sebagai asinkron, bisa di-suspendawait— titik di mana fungsi boleh di-suspend, menunggu hasilasync throws— fungsi asinkron yang bisa gagal
Kapan Menggunakan async/await
Gunakan:
- Operasi I/O: network request, baca/tulis file, database query
- Fungsi yang memanggil fungsi async lain
- Mengganti completion handler yang sudah ada
Jangan gunakan:
- Komputasi CPU-intensive murni (gunakan
Task.detachedatauDispatchQueueglobal) - Kode yang harus backward-compatible sampai iOS 14 atau lebih lama
Real Use Case: NetworkService dengan URLSession
swift
// Models — Sendable agar aman dikirim lintas thread
struct User: Decodable, Sendable {
let id: String
let name: String
let email: String
}
struct Post: Decodable, Sendable {
let id: Int
let title: String
let body: String
}
// NetworkService — kumpulan pure async function
struct NetworkService {
private let baseURL = URL(string: "https://jsonplaceholder.typicode.com")!
private let decoder = JSONDecoder()
func fetchUser(id: String) async throws -> User {
let url = baseURL.appendingPathComponent("users/\(id)")
let (data, response) = try await URLSession.shared.data(from: url)
guard let http = response as? HTTPURLResponse,
(200..<300).contains(http.statusCode) else {
throw NetworkError.badResponse
}
return try decoder.decode(User.self, from: data)
}
func fetchPosts(userId: String) async throws -> [Post] {
var components = URLComponents(url: baseURL.appendingPathComponent("posts"),
resolvingAgainstBaseURL: false)!
components.queryItems = [URLQueryItem(name: "userId", value: userId)]
let (data, _) = try await URLSession.shared.data(from: components.url!)
return try decoder.decode([Post].self, from: data)
}
}
enum NetworkError: Error {
case badResponse
}