こんにちは!コネヒトでiOSエンジニアをやっていますyanamuraです。
Xcode13.2からSwift ConcurrencyがiOS13からでも使えるようになり、遅ればせながらママリのiOSアプリでSwift Concurrencyを導入してみました。
ママリのアーキテクチャ
現在ママリのiOSアプリのアーキテクチャはMVVMになっています。 全体的にRxSwiftを用いてView, ViewModelのbindingだけでなく非同期処理を実装していて、新しく追加したところはCombineを使っているといった状況です。
どこに導入したか
今回Swift Concurrencyを導入したのは、Web APIとの通信部分です。これまではRxSwiftやCombineを用いて通信の非同期処理を行なっていました。しかし、基本的やっていることは通信して終わったら処理をするといった単純なことで、reactiveなことをしなくてcompletion handlerやResult型などでも事足りる感じでありました。Swift Concurrencyを導入することでasync/awaitでシンプルに書け、RxSwiftの依存部分を減らすことができないかと考え今回試してみました。
実装
通信周りは外部ライブラリに頼らずシンプルにURLSessionで実装していたので、以下のようなfunctionを用意してasync/awaitを使えるようにしました。
// 例 (見やすくするためかなり端折ったものでこのまま使わないでください) func request(url: URL) async throws -> Data? { try await withCheckedThrowingContinuation { continuation in let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, response, error in if let error { continuation.resume(with: .failure(error)) } else { continuation.resume(with: .success(data)) } } task.resume() } }
呼び出す時はawaitすることで同期っぽく書けるようになります。
let data = try await API().request(url: URL(string: "https://connehito.com")!)
このawaitする関数自体も結果待ちする場合はasyncし
func fetchUserID() async -> UUID { let data = try await API().request(url: URL(string: "https://connehito.com")!) … // dataをparseしてuserIDを取得 return userID }
結果待ちしなくてもいい場合はTaskで投げっぱなしにします。
func refresh() { Task { let data = try await API().request(url: URL(string: "https://connehito.com")!) …. // dataをparseしてuserIDを取得 self.userID = userID } }
まとめ
RxSwiftだとこんな感じだったものが
func fetchUserID() -> Observable<UUID> { return API().request(url: URL(string: “https://connehito.com”)!) .map { data in … // dataをparseしてuserIDを取得 return userID } }
async/awaitを使うと直感的な記述になりました。
func fetchUserID() async -> UUID { let data = try await API().request(url: URL(string: "https://connehito.com")!) … // dataをparseしてuserIDを取得 return userID }
一部に導入してみて割と良い感じだったので、どんどん使っていきたいと思います!
コネヒトでの開発に興味を持っていただいた方はカジュアルにお話しましょう〜 TwitterのDMなどでも大丈夫ですのでお気軽にどうぞ 日本中の家族をITの力で笑顔にしたい、iOSエンジニア募集! - コネヒト株式会社のモバイルエンジニアの採用 - Wantedly