Skip to content

Instantly share code, notes, and snippets.

@atierian
Created November 8, 2021 16:41
Show Gist options
  • Save atierian/01201212e7a7e2bc2b2867029491db9d to your computer and use it in GitHub Desktop.
Save atierian/01201212e7a7e2bc2b2867029491db9d to your computer and use it in GitHub Desktop.
Going from async to completion handlers.
import UIKit
enum SomeError: Error {
case foo, bar, baz, unknown
}
struct SDK {
func simpleAsync() async -> Int {
try? await Task.sleep(seconds: 2)
return 42
}
func asyncThrows() async throws -> Int {
try await Task.sleep(seconds: 2)
if Bool.random() { return 25 }
throw SomeError.foo
}
func asyncResult() async -> Result<String, SomeError> {
try? await Task.sleep(seconds: 2)
return Bool.random()
? .success("Hello World")
: .failure(.foo)
}
}
struct FrameworkManualExamples {
let sdk: SDK
func simpleHandlerPassthrough(_ completion: @escaping (Int) -> Void) {
Task.init {
let value = await sdk.simpleAsync()
completion(value)
}
}
func throwingHandlerPassthrough(_ completion: @escaping (Int) throws -> Void) {
Task.init {
try completion(await sdk.asyncThrows())
}
}
func resultHandlerPassthrough(_ completion: @escaping (Result<String, SomeError>) -> Void) {
Task.init {
let result = await sdk.asyncResult()
completion(result)
}
}
func asyncThrowsToResult(_ completion: @escaping (Result<Int, SomeError>) -> Void) {
Task.init {
do {
let value = try await sdk.asyncThrows()
completion(.success(value))
} catch let error as SomeError {
completion(.failure(error))
} catch {
completion(.failure(.unknown))
}
}
}
}
// MARK: Generic async -> completion handler helpers
enum AsyncHelpers {
static func simpleHandlerPassthrough<T>(
asyncMethod: @escaping () async -> T,
completion: @escaping (T) -> Void
) {
Task.init {
completion(await asyncMethod())
}
}
static func resultHandlerPassthrough<Success, Failure: Error>(
asyncMethod: @escaping () async -> Result<Success, Failure>,
completion: @escaping (Result<Success, Failure>) -> Void
) {
Task.init {
completion(await asyncMethod())
}
}
static func asyncThrowsToResultTransformer<Success, Failure: Error>(
asyncMethod: @escaping () async throws -> Success,
completion: @escaping (Result<Success, Failure>) -> Void,
transform: @escaping (() async throws -> Success) async -> Result<Success, Failure>
) {
Task.init {
await completion(transform(asyncMethod))
}
}
static func asyncThrowsToResultUnknownError<Success, Failure: Error>(
asyncMethod: @escaping () async throws -> Success,
completion: @escaping (Result<Success, Failure>) -> Void,
unknownErrorHandler: @escaping (Error) -> Failure
) {
Task.init {
do {
let value = try await asyncMethod()
completion(.success(value))
} catch let error as Failure {
completion(.failure(error))
} catch {
completion(.failure(unknownErrorHandler(error)))
}
}
}
}
// MARK: Examples of Framework Usage
struct FrameworkGenericHelperExamples {
let sdk: SDK
func asyncHelpersSimpleHandlerPassthrough(_ completion: @escaping (Int) -> Void) {
AsyncHelpers.simpleHandlerPassthrough(
asyncMethod: sdk.simpleAsync,
completion: completion
)
}
func resultHandlerPassthrough(_ completion: @escaping (Result<String, SomeError>) -> Void) {
AsyncHelpers.resultHandlerPassthrough(
asyncMethod: sdk.asyncResult,
completion: completion
)
}
func asyncThrowsToResultTransformer(_ completion: @escaping (Result<Int, SomeError>) -> Void) {
AsyncHelpers.asyncThrowsToResultTransformer(
asyncMethod: sdk.asyncThrows,
completion: completion,
transform: { asyncThrowingMethod in
do {
let value = try await asyncThrowingMethod()
return .success(value)
} catch let error as SomeError {
return .failure(error)
} catch {
// Handle unknown error
return .failure(.unknown)
}
}
)
}
func asyncThrowsToResultUnknownError(
completion: @escaping (Result<Int, SomeError>) -> Void
) {
AsyncHelpers.asyncThrowsToResultUnknownError(
asyncMethod: sdk.asyncThrows,
completion: completion
) { unknownError in
// logic to return specific error type based on `unkownError`
return .unknown
}
}
}
// MARK: Customer Callsite Examples
let frameworkGenericHelperExamples = FrameworkGenericHelperExamples(sdk: SDK())
frameworkGenericHelperExamples.asyncHelpersSimpleHandlerPassthrough {
log($0)
}
frameworkGenericHelperExamples.resultHandlerPassthrough { result in
switch result {
case .success(let value): log(value)
case .failure(let error): log(error)
}
}
frameworkGenericHelperExamples.asyncThrowsToResultTransformer { result in
switch result {
case .success(let value): log(value)
case .failure(let error): log(error)
}
}
extension Task where Success == Never, Failure == Never {
static func sleep(seconds: Double) async throws {
let duration = UInt64(seconds * 1_000_000_000)
try await Task.sleep(nanoseconds: duration)
}
}
func log<T>(_ v: T, line: Int = #line, function: StaticString = #function) {
print("[line: \(line)][function: \(function)] >>", v)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment