I had to write this little extension for a personal project. Then I couldn't help wondering why this is not already part of Swift? Because to me it seems like a missing piece of the puzzle without which the move towards "async await" wouldn't be complete. It looks like such a glaring omission that I keep wondering if there is good reason why this should not be done? Also asked a question here
extension Result where Failure == Error {
// Write an async version of the init that takes a throwing closure. In this case the throwing
// closure is an async function.
init(catchingAsync closure: @escaping () async throws -> Success) async {
// Call the async function and await the result.
do {
let value = try await closure()
self = .success(value)
} catch {
self = .failure(error)
}
}
// An async version of map. This is a function that takes a throwing closure and returns a Result.
// in this case the throwing closure is an async function.
func asyncMap<T>(_ transform: @escaping (Success) async throws -> T) async -> Result<T, Error> {
switch self {
case let .success(value):
do {
let transformedValue = try await transform(value)
return .success(transformedValue)
} catch {
return .failure(error)
}
case let .failure(error):
return .failure(error)
}
}
//An async version of flatMap. This is a function that takes a throwing closure and returns a Result.
// in this case the throwing closure is an async function.
func asyncFlatMap<T>(_ transform: @escaping (Success) async throws -> Result<T, Error>) async -> Result<T, Error> {
switch self {
case let .success(value):
do {
let transformedValue = try await transform(value)
return transformedValue
} catch {
return .failure(error)
}
case let .failure(error):
return .failure(error)
}
}
// An async version of mapError. This is a function that takes a throwing closure and returns a Result.
// in this case the throwing closure is an async function.
func asyncMapError(_ transform: @escaping (Error) async throws -> Error) async -> Result<Success, Error> {
switch self {
case let .success(value):
return .success(value)
case let .failure(error):
do {
let transformedError = try await transform(error)
return .failure(transformedError)
} catch {
return .failure(error)
}
}
}
// An async version of flatMapError. This is a function that takes a throwing closure and returns a Result.
// in this case the throwing closure is an async function.
func asyncFlatMapError(_ transform: @escaping (Error) async throws -> Result<Success, Error>) async -> Result<Success, Error> {
switch self {
case let .success(value):
return .success(value)
case let .failure(error):
do {
let transformedError = try await transform(error)
return transformedError
} catch {
return .failure(error)
}
}
}
}