Swiftにも5.5でついにSwift Concurrencyが導入されたので一番有用そうなAPI取得周りをasync/awaitで記述できるようにしてみます。
今回はSwiftの通信ライブラリとしてスタンダードであろうAlamofire+Codableを環境として用いて実装しています。
(そのうちAlamofire公式にもasync/awaitで受け取れる口は出来るとは思いますが現時点ではiOS15以降が要求されるのでしばらくは入らないかと思います)
AlamofireのExtensionを記述する
Alamofireはクロージャで結果を受け取とれるので、これをasync/awaitに変換します。
旧来のクロージャ構文を変換するにはwithCheckedThrowingContinuationを利用します。
import Foundation
import Alamofire
extension DataRequest {
func publish<T>(_ type: T.Type) async throws -> T where T : Decodable {
try await withCheckedThrowingContinuation { continuation in
self.response { response in
switch response.result {
case .success(let element): do {
let decodedResponse = try JSONDecoder().decode(type, from: element!)
continuation.resume(returning: decodedResponse)
} catch {
continuation.resume(throwing: error)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}
continuation.resume
を呼ぶことで呼び出し元へ結果を返して処理をすすめる事ができます。
実際に呼び出してみる
あとは生やしたメソッドを呼び出すだけでasync/awaitへの変換は完了します。
普段どおりCodableを実装したstructを用意してください。
import Foundation
struct GithubUser: Codable, Equatable, Identifiable {
let id: Int
let name: String
let htmlUrl: String
let avatarUrl: String
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case htmlUrl = "html_url"
case avatarUrl = "avatar_url"
}
}
その上で、Alamofireのリクエストクラスから作成したpublish()
を呼びだしてください。
struct GithubApi {
func fetchUser(userName: String) async throws -> GithubUser {
try await AF.request("https://api.github.com/users/\(userName)").publish(GithubUser.self)
}
}
withCheckedThrowingContinuationとwithCheckedContinuation
上記ではクロージャの変換用にwithCheckedThrowingContinuationを紹介しましたが、
これとは別に withCheckedContinuationという関数もあります。
こちらはErrorをThrowすることがない場合に利用できます。こちらの関数ではtryの指定が不要になるので処理が失敗しうるかどうかで使い分けてください!