【Swift】コールバック関数を async / await に変換【Concurrency】
WWDC2021 で発表された Concurrency (並行処理)が、ついにiOS 13でも使えるようになりました。
そのおかげで iOS13 からでも非同期処理、並行処理のコードをより簡潔に、より安全に書くことができるようになりました。
しかし、全てのメソッドがすぐに async
/ await
になるわけではありません。
自作メソッドやサードパーティライブラリの非同期APIがコールバックのままになっているかもしれません。
手元にあるのはコールバック関数だけで、 async
/ await
ができる関数が今すぐにでも欲しい!!
そんなあなたに向けてこの記事を書きました。
環境
Xcode 13.2.1
Swift 5.5
結論
標準ライブラリに新たに登場した withCheckedContinuataion
または withCheckedThrowingContinuation
という関数を使います。
withCheckedContinuationはこちら
https://developer.apple.com/documentation/swift/3814988-withcheckedcontinuation
withCheckedThrowingContinuationはこちら
https://developer.apple.com/documentation/swift/3814989-withcheckedthrowingcontinuation
引数がシンプルなコールバックの場合
例として、func downloadURL(completion: (URL) -> ())
を async
/ await
に対応させてみます。
throws
は必要ないので、 withCheckedContinuation
関数を使って async
/ await
対応させてみましょう
// 変換前の関数
func downloadURL(completion: (URL) -> ()) // (1)
// 変換後の関数
func downloadURL() async -> URL {
await withCheckedContinuation { continuation in
downloadURL { url in
continuation.resume(returning: url)
}
}
}
Throwsが必要なコールバックの場合
Errorが帰ってくるかもしれない非同期処理の場合は、 withCheckedThrowingContinuation
関数を使いましょう。
func downloadData(completion: (Data?, Error?) -> ())
を async
/ await
に対応させてみます。
// 変換前の関数
func downloadData(from url: URL,
completion: @escaping (Data?, Error?) -> Void) // (2)
// 変換後の関数
func downloadData(from url: URL) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
downloadData(from: url) { data, error in
if let error = error {
continuation.resume(returning: data)
} else {
continuation.resume(throwing: error)
}
}
}
}
コールバックがResult型の場合
コールバックがResult型の場合は、さらに簡潔に書けるようになります。
func downloadData(completion: (Result<Data, Error>) -> ())
を async
/ await
に対応させてみます。
// 変換前の関数
func downloadData(completion: (Result<Data, Error>) -> ())
// 変換後の関数
func donwloadData() async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
downloadData(from: URL) { result in
continuation.resume(with: result)
}
}
}
まとめ
コールバック関数を throws
する必要がなければ withCheckedContinuation
関数を。 throws
する必要があれば、 withCheckedThrowingContinuation
関数を使ってラップしてあげましょう。
間違っている点があれば、DMで教えてください!!
twitter: @_armtic
Instagram: @armtic
Scrapbox: /armtic
Discussion