Created
November 8, 2021 16:41
-
-
Save atierian/01201212e7a7e2bc2b2867029491db9d to your computer and use it in GitHub Desktop.
Going from async to completion handlers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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