ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Swift Async Algorithmsについて
Appleの最新のオープンソースSwiftパッケージ「Swift Async Algorithms」を紹介します。このパッケージより、Zip、マージ、スロットルなど、AsyncSequenceで使用できるアルゴリズムについて解説します。これらのアルゴリズムで優れたメッセージングAppを構築しますので、是非ともご確認ください。また、複数のAsyncSequencesと組み合わせたり、Swift Clockタイプで経時的に値を連携させたりするベストプラクティスも紹介します。 このセッションを最大限に活用するには、「AsyncSequenceについて」をご確認ください。
リソース
関連ビデオ
WWDC22
WWDC21
-
ダウンロード
♪ ♪
こんにちは Philippeです Swiftで オープンソースの パッケージカタログが増え続けています 最新の追加パッケージの1つを 紹介することを嬉しく思います Swift Async Algorithmsです このパッケージは Swift Collectionsや Swift Algorithms など 他パッケージと並んでおり Swift Async Algorithmsパッケージは 特に AsyncSequenceを使用し 時間をかけて値を処理することに 焦点を当てたアルゴリズムです しかしその前に AsyncSequenceについて 簡単におさらいしましょう AsyncSequenceは 非同期に生成される値を 記述するための プロトコルです 基本的にはSequenceと同じですが 2つの重要な違いがあります そのイテレータから 次の関数は Swiftの並行処理で 値を配信できる為 非同期です Swiftのスロー効果を使い 失敗の可能性がある物を処理できます シーケンスと同様に for-await-in構文を使って 繰り返し実行することが できます つまりSequenceの使い方を 知っていれば AsyncSequenceの使い方は すでに知っているのです AsyncSequenceが導入され Sequenceに期待される ほぼ全てのツールが非同期版で そのまま追加されました map filter reduceなどの アルゴリズムがあります Swift Async Algorithmsパッケージは より高度なアルゴリズムを取り入れ さらに一歩前進し またクロックとの相互運用で 強力なものを提供が できるようになりました Swiftの並行処理を補強する AsyncSequenceアルゴリズムの オープンソース パッケージです 昨年 Swift Algorithmsの パッケージを導入しました それらのアルゴリズムの 使い方を実証するため メッセージングAppを 作りました そのパッケージで できることの豊かさ 力強さを実感しました Swiftの並行処理を使うため Appを移行することで 本当に良い機会がいくつも あると判断したのです 非同期アルゴリズムの いくつかを紹介するために 私たちが使用したものと その動作を 説明します まず最初に 複数の入力AsyncSequenceを 扱うための アルゴリズム群があります AsyncSequenceを様々な方法で 組み合わせるアルゴリズムです ある共通した特徴があります: 複数の入力AsyncSequenceを受け 1つの出力AsyncSequenceを生成します
ご存知の方も多いかも しれませんが Zipです Zipアルゴリズムは 複数の入力を受け取り 各ベースからの結果のタプルを 生成するように反復します Zipの各入力は Zipを構成する ベースとなるものです 非同期Zipアルゴリズムは 標準ライブラリZipアルゴリズムと 同様に動作しますが 各ベースを同時に反復処理し いずれかの反復処理に 失敗した場合は エラーを再スローします さて このように エラーを出しながら 同時並行的に反復することは かなり大変なことです しかし Swift Async Algorithms パッケージは メッセージAppのため そのすべてを 引き受けてくれました 以前は 録画した ビデオプレビューを 非同期で生成したり ビデオを複数のサイズに トランスコードして効率的に 保存 転送するためのコードを 多数用意していました Zipを使用することで トランスコードビデオをサーバに送信時 プレビューを確実に 表示することができます Zipは同時進行なので トランスコードもプレビューも 互いに 遅れることはありません しかし これは もう少し先の話です Zip自身は どちらが先に 値を生成したか優先せず ビデオが先に生成されたり プレビューが生成されたり それがどちらであっても 相手が完全なタプルを 送信するのを待つことになります Zipは値のタプルを構築するため 各サイドを同時に待ち受けるので 一緒にアップロードできるよう ペアを待ち受けることができます 受信メッセージをAsyncSequence としてモデル化することは 非常に理にかなっている という結論に達しました メッセージ処理のために AsyncStreamを使用しました これは 順序を維持し コールバックをメッセージの AsyncSequenceに変える為です 取り組むべき要望のひとつに 「複数アカウントに対応したい」 というものがありました 各アカウントは受信メッセージの AsyncStreamを作成しますが これを実装する場合 まとめて1つのAsyncSequenceとして 処理する必要があります それらAsyncSequenceを マージするアルゴリズムが必要でした Swift Async Algorithmsパッケージには そのためのアルゴリズムがあり その名も「マージ」と 名付けられています 複数のAsyncSequencesを 同時に反復する点では Zipと同様ですが ペアの タプルを作成するのではなく ベースが同じ要素タイプを 共有する必要があり ベースのAsyncSequencesを それらの要素の単一の AsyncSequenceに マージするものです Mergeは 反復処理された際 どちらかが生成する最初の 要素を取ることで機能します 生成可能な値がなくなるまで 具体的には すべてのベース AsyncSequencesがイテレータ からnilを返すまで反復し続けます あるベースでエラーが発生した場合 他のイテレーションはキャンセルされます メッセージのAsyncSequences を取り それらをマージできます 結合アルゴリズムは 値が生成される タイミングに並行して作業しますが 時には時間そのものと インタラクションすることが有効です Swift Async Algorithmsは Swiftの新しい Clock API を利用し 時間アルゴリズムの ファミリーをもたらします 時間そのものは 本当に複雑な主題であり Swift (5.7) の 新しいAPIは それを安全かつ 一貫したものです クロック インスタント デュレーションです
Clockプロトコルは 与えられた瞬間後に起きる方法と 現在という概念を 生み出す方法の 2つのプリミティブを 定義しています いくつかのビルトイン Clockがあります 代表的なものに ContinuousClockと SuspendingClock があります ContinuousClockを使えば ストップウォッチのように 計測媒体の状態に関係なく 時間が進む計測が可能です 一方SuspendingClockは その名の通り マシンがスリープ状態になると サスペンドされます このAppでは 新しいClock APIを 使い 既存コールバックイベントから Clockのスリープ関数に移行し 締め切り後アラートを解除しました 何秒遅らせたいかを示す duration値を追加することで デッドラインを 作成することができます Clockは 仕事の実行時間の 経過を測定する便利な メソッドも持っています 紹介した共通クロック SuspendingClockと ContinuousClock を使用しています
以下は 測定される作業の 潜在的な経過時間を 示す表示です この2つのクロックの 重要な違いは マシンがスリープ時の 動作にあります
このような長時間の作業には 今回のように作業を 一時停止することも可能です しかし 実行を再開すると マシンがスリープ中の間に ContinuousClock は進行しますが SuspendingClockは 進行しません 一般的にこの違いは アニメーションの実行タイミングを 一時停止することで 期待通りの動作をさせる 重要なディテールと なることがあります アニメーションのように マシーンとの関係でtime inを 操作する必要がある場合 SuspendingClockを使用します
デバイスの前にいる人間との 関係でタスクを計測するのは ContinuousClock の方が適しています 絶対的な時間 つまり人間 にとって相対的なもので 遅延させる必要がある場合 ContinuousClockを使用します Swift Async Algorithmsは これらの新しい Clock Instant Duration タイプを使用し イベントが時間を処理する方法の 概念の多くを扱うための 一般的アルゴリズムを構築します メッセージAppでは イベントを正確に制御する為 これらがとても 役に立ちました インタラクションを制限し 効率的にバッファできます 最も時間を使ったのは メッセージの 検索です 結果のチャンネルを管理する コントローラを作成しました チャネルは 検索タスクから UIに戻る検索結果を整理します 検索タスク自体にも 時間に関するいくつかの 特徴が必要でした サーバでの送信メッセージの 検索をレート制限したかったのです
アルゴリズムDebounceは 繰り返し実行された場合 次の値を出力する前に 静止期間を待ちます イベントがどんどん 入ってくるということですが 静寂の時を待って 値を扱うようにしたいのです 検索フィールドからの ユーザ入力が急激に変更された場合 変更のたびに 検索コントローラが 検索リクエストを 起動するのは避けたいです その代わり タイピングが 行われそうだと 確信できる閑散期を 待つようにしたいのです デフォルトでDebounceは ContinuousClockを使用します この場合 何も起きていない状態で 指定した時間だけ 入力を待つデバウンスを 行うことができます クロックとデュレーションは デバウンスだけでなく 他のアルゴリズムにも 使われます その中で特に 便利だと感じたのは サーバへのメッセージの 一括送信です Swift Algorithms パッケージの中に 値をチャンクするための アルゴリズム群があります Swift Async Algorithmsは それらを提供するだけでなく クロックとデュレーションと 相互運用するバージョンの セットも追加しています チャンキングアルゴリズムの ファミリーは チャンクを数 時間 または内容で 制御することができます エラーが発生した場合 そのエラーは再スローされ 私たちのコードは 失敗しても安全です chunked(by:) APIを使用し メッセージチャンクがシリアライズされ 特定の経過時間までに 送信されるようにしました サーバはクライアントから送られる パケットを効率よく受け取れます このAPIを使い 500ミリ秒 ごとにメッセージバッチを ビルドすることが できたのです 誰かが興奮して 速くタイピングしても サーバに送られるリクエストは グループ化されるのです コレクションや シーケンスを扱う場合 要素の遅延処理は便利で パフォーマンスも上がります AsyncSequenceはSwift 標準ライブラリで 遅延アルゴリズムがどう 動作するかによく似ています そのLazyなアルゴリズムと同じく コレクションの世界に 戻らなければならないことも しばしばあります Swift Async Algorithmsは AsyncSequence を使って コレクションを構築する イニシャライザのセットを提供します 有限なのが分かっている AsyncSequencesを入力として 辞書 セット または配列を 構築することができます コレクションイニシャライザを使えば メッセージの初期化時に 変換を組み込み データ型を Arrayのままにしておけます Swiftの並行処理を使うために 本当に更新が必要な機能が 数多くあったので これは本当に便利でした 既存のデータ構造を 維持することで Appの一部を段階的に そして意味のある場所に 移行することができます ここまで Swift Async Algorithmsパッケージの ハイライトのほんの一端を 紹介したに過ぎません 今日取り上げたもの以外にも まだまだたくさんあります 複数のAsyncSequence の組み合わせ 時間レート制限 チャンクへの分割など さまざまなアルゴリズムが ありますが これらは 私たちのAppの広範囲に 使用することになった ハイライトにすぎません このパッケージには それ以外に 様々な機能が搭載されています バッファリング リデュース ジョイン 断続的な値の注入など 多岐にわたります Swift Async Algorithms パッケージは 時間をかけて物事を処理する 一連のアルゴリズムを Appで役立つ幅広い 高度な機能へと拡張した物です 試してみてください 皆さんがどんなものを作るのか とても楽しみにしていますし その興奮は共通しています このパッケージは 皆さんと共に オープンで開発しています ご視聴ありがとうございました 残りのカンファレンスもお楽しみください ♪ ♪
-
-
2:01 - The messaging app
struct Account { var messages: AsyncStream<Message> } actor AccountManager { var primaryAccount: Account var secondaryAccount: Account? } protocol MessagePreview { func displayPreviews(_ manager: AccountManager) async }
-
3:16 - Zip
// upload attachments of videos and previews such that every video has a preview that are created concurrently so that neither blocks each other. for try await (vid, preview) in zip(videos, previews) { try await upload(vid, preview) }
-
5:09 - Merge
// Display previews of messages from either the primary or secondary account for try await message in merge(primaryAccount.messages, secondaryAccount.messages) { displayPreview(message) }
-
6:37 - Suspending Clock
// Sleep until a given deadline let clock = SuspendingClock() var deadline = clock.now + .seconds(3) try await clock.sleep(until: deadline)
-
6:56 - Suspending Clock vs. Continuous Clock
let clock = SuspendingClock() let elapsed = await clock.measure { await someLongRunningWork() } //Elapsed time reads 00:05.40 let clock = ContinuousClock() let elapsed = await clock.measure { await someLongRunningWork() } //Elapsed time reads 00:19.54
-
8:34 - Control searching messages
// Control searching messages class SearchController { let searchResults = AsyncChannel<SearchResult>() func search<SearchValues: AsyncSequence>(_ searchValues: SearchValues) where SearchValues.Element == String }
-
9:16 - Debounce
let queries = searchValues .debounce(for: .milliseconds(300)) for await query in queries { let results = try await performSearch(query) await channel.send(results) }
-
10:21 - Chunked by
let batches = outboundMessages.chunked( by: .repeating(every: .milliseconds(500)) ) let encoder = JSONEncoder() for await batch in batches { let data = try encoder.encode(batch) try await postToServer(data) }
-
11:22 - Conversions in initializers
// Create a message with awaiting attachments to be encoded init<Attachments: AsyncSequence>(_ attachments: Attachments) async rethrows { self.attachments = try await Array(attachments) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。