LFK mobile DevPods

Presenting the latest mobile development and life of Engineers in LINE Fukuoka, Japan

  • freddi
  • shinzan
Published: 2021/06/17 1500 views

About this episode

最近WWDCでも発表された今盛り上がっている Swift のasync/await について, shinzan さんと初心者に向けて詳しく解説

オープニング

  • shinzanさん
    • 紹介記事: https://linefukuoka.blog.jp/archives/20201030_engineer_interview_01.html
    • LINE AppのSHOPチームの開発メンバー
    • 具体的にはスタンプに関わる機能を担当している(LINEスタンプの利用・購入など)
    • 入社経緯
      • コロナが流行り始めた頃に転職活動を始め、LINE Fukuokaが東京で主催したコロナ前最後のオフライン採用イベントにたまたま参加
      • そのイベントで興味が湧き、面接を通して入社を決意

async/await について

  • 2017年のChris Lattner氏のマニフェストにて提唱される
  • 結局それに手がつけられたのは2020年末
    • ロードマップが2020年10月頃発表、そこから各機能のPitchなどが始まる
  • WWDC2021でSwift5.5の新機能として発表され、 Xcode13 Betaにて登場。しかし現在(2021年6月段階)でもレビュー段階のままなので今後も変わる可能性
    • WWDC2021のセッションから変更する予定のものもあって注意が必要 (例えば、Structured Concurrency といった機能もシンタックスが変わる)
  • Swift6に向けてどうするか、という話がSwift Forumsにあがっている

Swift Concurrency が解決する問題

  • クロージャ特有の問題
    • DispatchQueueを利用する際のクロージャによるネストの深さがなくなる
      • いわゆるクロージャ地獄
    • completionHandlerの問題が解決される
      • completionHandler 呼び忘れ
      • completionHandler の後の return 呼び忘れ
  • Race Conditionを防ぐことができる
    • いままでDispatchQueueでやっていたところがactorに代わってくれる
    • 型レベルでのデータの保護
  • いままでSwiftを書く人が頑張ってたところが、コンパイラがサポートしてくれるようになる

async/await に代わっていくコード

実際に読むコード

https://developer.apple.com/videos/play/wwdc2021/10132/ より抜粋

  • async/await 無し
func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
    let request = thumbnailURLRequest(for: id)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            completion(nil, error)
        } else if (response as? HTTPURLResponse)?.statusCode != 200 {
            completion(nil, FetchError.badID)
        } else {
            guard let image = UIImage(data: data!) else {
                completion(nil, FetchError.badImage)
                return
            }
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
                guard let thumbnail = thumbnail else {
                    completion(nil, FetchError.badImage)
                    return
                }
                completion(thumbnail, nil)
            }
        }
    }
    task.resume()
}
  • async/await 有り
func fetchThumbnail(for id: String) async throws -> UIImage {
    let request = thumbnailURLRequest(for: id)
    let (data, response) = try await URLSession.shared.data(for: request)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw FetchError.badID }
    let maybeImage = UIImage(data: data)
    guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
    return thumbnail
}

実際に見ていてどうか?

  • async/await 有り のほうが圧倒的に行数が少ないし、ネストが深くない
  • async/await 有り は純粋に純粋に上から下に処理が流れる
  • async/await 無しは古いURLSessionなので、resume()関数を呼ばなければならない
  • async/await という単語がところどころにある。単語を書く順番も決まっている
    • async throws
    • try await
  • URLSessionのdataTask関数を呼んで結果が帰ってきたときの挙動としてクロージャを定義するスタイルから、URLSessionのdata関数を呼んでそのまま結果をawaitして得るスタイルへ
    • dataTaskのクロージャの引数は3つ(Data, Responce, Error)はすべてOptional型なので、自分でBindingしてエラーハンドルしないといけない
    • dataは(Data, Responce)はnon-Optional型、 エラーはtry越しで返ってくる
    • dataTaskのクロージャの中でDispatchQueueをまた読んでネストが深くなる・・・ということもdataではなくなる
  • データや処理の流れもひと目見てわかりやすい

async/await を学ぶ際に・・・

  • async/await(Swift Concurrency) はiOS15のみ
  • 公式にiOS14以下が切られる時、もしくはアプリで(ビジネス的に許されるなら)iOS14を切るという対応も一つの手
    • if avaibaleなどで分岐してもいいけどメンテナンスコスト高くなりそう
  • (2021/Sep 追記) Back Deployment のPRがマージされました https://github.com/apple/swift/pull/39051
  • 初心者がasync/awaitを学ぶには?
    • WWDC2021のセッションは豊富なので見てほしい
    • DispatchQueueへのリプレイスのための勉強はそこまで苦ではない
  • 初心者がそもそも非同期という概念を学ぶには?
  • 英語への抵抗
  • ちょっとしたshinzanさん的まとめ(学ぶおすすめの順番)
    1. async/await → 非同期処理を同期的な処理と同じような方法で、簡潔にわかりやすく、ミス少なく書くことができる
    2. Structured Concurrency → async/awaitを使って同時並行(concurrency)処理をよりわかりやすく(内部的に)効率よく、扱うことができる
    3. actor → 同時並行(concurrency)処理の中でもデータ競合(複数スレッドから同じデータを変更しようとすること)を防ぐことができる
  • https://developer.apple.com/videos/play/wwdc2021/10194/ もおすすめ
    • サンプルアプリをステップ・バイ・ステップでasync/awaitに対応する というセッション

エンディング

  • 感想等は #LFK_DEVPODS へ!
10秒前へ 10秒次へ