Section 4/91 menit
4. Typed Throws dengan Generics
4. Typed Throws dengan Generics
Ini adalah killer feature dari typed throws — memungkinkan generic functions yang meneruskan error type secara transparan:
Pattern: Error Propagation di Generic Function
swift
// Sebelum Swift 6: harus throws(any Error), kehilangan tipe error
// extension Array {
// func myMap<T>(_ transform: (Element) throws -> T) throws -> [T] { ... }
// }
// Swift 6: error type dari transform di-propagate secara tepat
extension Array {
func myMap<T, E: Error>(_ transform: (Element) throws(E) -> T) throws(E) -> [T] {
var result: [T] = []
result.reserveCapacity(count)
for element in self {
result.append(try transform(element))
}
return result
}
}
// Penggunaan 1: non-throwing transform → myMap tidak throws
let doubled = [1, 2, 3].myMap { $0 * 2 } // tidak perlu try!
print(doubled) // [2, 4, 6]
// Penggunaan 2: typed throwing transform → myMap propagates error type
enum ConversionError: Error { case overflow }
let parsed = try [" 1", "2", "3"].myMap { str throws(ConversionError) -> Int in
guard let n = Int(str.trimmingCharacters(in: .whitespaces)) else {
throw ConversionError.overflow
}
return n
}
// catch block tahu persis error-nya adalah ConversionError
Pattern: Result Type dengan Typed Error
swift
// Result<Value, E> sudah ada — sekarang bisa berpasangan dengan typed throws
func riskyOperation() throws(DomainError) -> Value {
// ...
}
// Konversi mudah antara throws dan Result
let result: Result<Value, DomainError> = Result { try riskyOperation() }
// Dan sebaliknya
let value = try result.get() // throws(DomainError)
Pattern: Async + Typed Throws
swift
actor NetworkClient {
enum RequestError: Error {
case noConnection
case timeout
case serverError(statusCode: Int)
case invalidResponse
}
func fetch(url: URL) async throws(RequestError) -> Data {
guard isConnected else { throw RequestError.noConnection }
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard let http = response as? HTTPURLResponse else {
throw RequestError.invalidResponse
}
guard (200...299).contains(http.statusCode) else {
throw RequestError.serverError(statusCode: http.statusCode)
}
return data
} catch is URLError {
throw RequestError.timeout
} catch {
throw RequestError.invalidResponse
}
}
private var isConnected: Bool { true }
}
// Penggunaan:
let client = NetworkClient()
do {
let data = try await client.fetch(url: someURL)
process(data)
} catch {
// error adalah NetworkClient.RequestError — tanpa cast
switch error {
case .noConnection: showOfflineUI()
case .timeout: showRetryUI()
case .serverError(let code): showServerError(code)
case .invalidResponse: showParseError()
}
}