Swift クロージャ・コールバック(コンプレーションハンドラー)のメソッドをasync,awaitでラップして使う

Swift Swift
Swift

async、awaitを使いたい

Swiftのasync、awaitを使ってコードを書いていると、
既存のプロジェクトのコードや、ライブラリなど、クロージャ・コールバック(コンプレーションハンドラー)の形で書いているコードを呼び出す場合があります。

その時に、そのままクロージャ・コールバック(コンプレーションハンドラー)の形でコードを書いても良いのですが、async、awaitの形で統一して書きたいです。
書きたいですよね??(;・∀・)

ただ、少しのコードであれば書き直すのですが、量が多かったり、書き直す事ができない場合があります。
その時にクロージャ・コールバック(コンプレーションハンドラー)のメソッドをラップして、書き換えずにasync、awaitに対応する方法。

コードはシンプルに最低限の事しか書いてませんので、処理内容に応じて記述が必要です。

※ネットで調べていると、iOS14でバグがあったとかの記述を見かけました。
個人的には問題に遭遇していないのですが、問題があるのかも・・・?しれないので、使われる時は注意が必要かもしれないです。
※個人的には今の2022年8月時点でiOS14を正式にサポートしなくても良いのでは・・・と思ったりしますが、どうなんでしょう・・・(´ε`;)

ラップする前の(今までクロージャ・コールバック(コンプレーションハンドラー)での)コード

func sessionRequest(urlText: String, completion: @escaping (Result<Data?, Error>) -> Void){
    
    if let url = URL(string: urlText) {
        
        let session = URLSession.shared.dataTask(with: url) { data, res, error in
            
            if let error = error {
//                失敗した時の処理
                completion(.failure(error))
            } else {
//                成功した時の処理
                completion(.success(data))
            }
            
        }
        
        session.resume()
    } else {
        completion(.failure(TestError.invalid("invalid url")))
    }
}
enum TestError: Error {
case invalid(String)
}

このような形でクロージャ・コールバック(コンプレーションハンドラー)の形で書いているメソッドがあるとします。
これくらいであれば、書き直す事もできますが、内容が複雑だったりしたら、触りたくないです。

そのため、ラップして中身を変えずに対応したい。

async、awaitに対応するためのコード

func sessinRequestAsync(urlText: String) async throws -> Data? {
    
    do {
        
        guard let url = URL(string: urlText) else {
            throw TestError.invalid("invalid url")
        }
        
        return try await withCheckedThrowingContinuation({ contiuation in
            
            let session = URLSession.shared.dataTask(with: url) { data, res, error in
                
                if let error = error {
                    contiuation.resume(throwing: error)
                } else {
                    contiuation.resume(returning: data)
                }
            }
            
            session.resume()
            
        })
        
    } catch {
        throw error
    }
}

このように、withCheckedThrowingContinuationで囲う事で、async、awaitに対応したコードに変化させる事ができます。

めっちゃ簡単ですよね・・・

withCheckedThrowingContinuationメソッドはエラーをスローする場合があるらしいので、tryを付けて呼び出す必要があります。

withCheckedThrowingContinuationメソッドはエラーをスローする場合がある処理に利用し、
withCheckedContinuationはメソッドはエラーをスローしない場合に利用するようです。

Apple Developerサイト Updating an App to Use Swift Concurrency

https://developer.apple.com/documentation/swift/updating_an_app_to_use_swift_concurrency

詳しくはアップルのDeveloperサイトなどを見て頂くのがわかりやすいです。
※アップルは本当によくDeveloperサイトの見た目を更新されますよね。

以上が既存のプロジェクトのコードをasync、awaitに対応させるための、シンプルなコードです。

コメント

タイトルとURLをコピーしました