iOSエンジニアの石田です。
WWDC2021では、ConcurrrencyにまつわるSwiftの新しい文法やSwiftUIの新しいViewが紹介されました。
本記事では、それらを使ったアプリを作りながら、新機能を紹介したいと思います。
作るアプリは、GitHubのAPIを使って、ユーザの画像をUICollectionViewのような形式で表示するものです。
ユーザ情報は https://api.github.com/users から取得でき、ユーザ画像は avatar_url
で定義されています。
async/await
まず、API経由でユーザ情報を取得します。
URLSessionを使って非同期通信を行う場合、コールバックでレスポンスを処理していました。 しかし、Swift5.5で追加されたasync/awaitを使うことでコールバックが不要になります。
async/await自体は他の言語でも実装されている文法で、非同期処理をより簡潔に記述することができます。
以下が例となります。 簡単のためにエラーハンドリングなどは全て無視しています。
func fetch() async { let users = try! await fetchUsers() } func fetchUsers() async throws -> [User] { let url = URL(string: "https://api.github.com/users")! let (data, _) = try await URLSession.shared.data(from: url) let users = try JSONDecoder().decode([User].self, from: data) return users }
関数の返り値として非同期処理の結果を返すことができます。
URLSessionの処理をawaitをつけて呼び出し、関数自体をasyncで定義しています。 asyncをつけた関数を呼ぶ際は、awaitをつけて呼びます。
async/awaitを利用することでコールバックを使うことなく、同期処理と同じ記述で非同期処理を書くことができます。
AsyncImage
もともとSwiftUIにはImageという画像表示のためのViewがありましたが、URLを直接入力することはできず、HTTP通信で画像をダウンロードする場合はひと手間加える必要がありました。
Xcode13で登場したAsyncImageではそういった手間は不要で、URLを入力すると画像が自動でダウンロードされます。
使い方は極めてシンプルで、例えば以下のようになります。
AsyncImage(url: URL(string: "https://example.com/icon.png"))
プレースホルダーを指定し、画像をリサイズする場合は以下のようになります。
AsyncImage(url: url) { image in image.resizable() } placeholder: { ProgressView() } .frame(width: 100, height: 100)
AsyncImageの引数はURL?型なので、unwrapすることなく気軽に使えるのも良いところだと思います。
async/awaitとAsyncImageを使ったサンプル
上述したasync/awaitとAsyncImage、そしてLazyVGridを使ってGitHubのユーザ画像をUICollectionViewのように2カラムでユーザ画像を表示します。
import SwiftUI struct User: Decodable, Identifiable { let id: Int let avatarUrl: String } struct ContentView: View { @State var users = [User]() let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 2) var body: some View { ScrollView { LazyVGrid(columns: columns) { ForEach(users) { user in AsyncImage(url: URL(string: user.avatarUrl)) { image in image .resizable() .scaledToFill() } placeholder: { ProgressView() } } } } .task { users = try! await fetchUsers() } } private func fetchUsers() async throws -> [User] { let url = URL(string: "https://api.github.com/users")! let (data, _) = try await URLSession.shared.data(from: url) let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase let users = try decoder.decode([User].self, from: data) return users } }
API通信など含めても非常に簡潔に書くことができました。
エラーハンドリングを全て無視しているので実際はこんなに少ない行数にはなりませんが、コールバックがないため見通しが良くなっていると思います。
まとめ
簡単なアプリの実装を通して、Xcode13で追加された機能を紹介しました。
iOS15からの機能なので、iOS14をサポートしている間は使うことができないのですが、使うのが非常に楽しみな機能になっています。
async/awaitの登場によってRxSwiftやCombineがどのように捉えられ、使われていくのかもウォッチしていきたいところです。
TRILLでは、今後さらにサービスを大きくすべくアップデートを進めており、エンジニア、デザイナー、PdMを積極採用中です。
もし興味がありましたら、気軽にアクセスしていただければと思います。