View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

WWDC25に戻る

  • 概要
  • トランスクリプト
  • コード
  • Swiftの並行処理の活用

    Swiftの並行処理の主要コンセプトを紹介します。並列処理はアプリの応答性とパフォーマンスの向上に役立ちます。Swiftは、非同期および並行のコードを簡単かつ適切に記述できるようにデザインされています。アプリをシングルスレッドから並行処理へ移行するための手順を紹介します。また、コードを非同期にする、バックグラウンドに移動する、同時に実行している複数のタスク間でデータを共有するなど、Swiftの並行処理機能のメリットを最大限に引き出すための方法と使用のタイミングについても解説します。

    関連する章

    • 0:00 - イントロダクション
    • 3:17 - シングルスレッドコード
    • 6:00 - 非同期タスク
    • 7:24 - インターリーブ
    • 10:22 - 並行処理の導入
    • 11:07 - 並行処理の関数
    • 13:10 - 非分離のコード
    • 14:13 - 並行スレッドプール
    • 14:58 - データの共有
    • 15:49 - 値型
    • 17:16 - アクターから分離された型
    • 18:30 - クラス
    • 23:18 - アクター
    • 26:12 - まとめ

    リソース

    • Swift Migration Guide
    • The Swift Programming Language: Concurrency
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC25

    • Code Along:Swiftの並行処理によるアプリの強化

    WWDC23

    • 構造化並行処理の基本を超えて
  • このビデオを検索

    こんにちは SwiftチームのDougです Swiftの並行処理をアプリで適切に 使用する方法についてお話しします 並行処理を使用するとコードで 複数のタスクを同時に実行できます アプリで並行処理を使用することで ディスクからファイルを読み取るときや ネットワーク要求を実行するときなど データ待機時の応答性を改善できます また 大きな画像の処理など負荷の高い 計算をバックグラウンドにオフロードする ためにも使用できます Swiftの並行処理モデルは 正しい並行処理コードを簡単に 記述できるように設計されています これにより 並行処理の 導入が明確になり どのデータを並行処理タスク間で 共有するかを特定できます この情報を活用してコンパイル時に 潜在的なデータ競合が特定されるため 修正が困難なデータ競合の 発生を心配することなく 必要に応じて並行処理を導入できます

    多くのアプリでは並行処理を 一部で使用するだけで済み 並行処理が必要ないアプリもあります 並行処理コードは シングルスレッドコードよりも複雑です 必要な場合にのみ並行処理を 導入すべきです

    アプリでは すべてのコードの実行を メインスレッドで開始する必要があり シングルスレッドコードで かなりの処理を実行できます メインスレッドではアプリが UI関連のイベントを受け取り それに応じてUIを更新できます アプリで大量の計算を行わない場合は すべてをメインスレッドに 記述しても問題ありません そのうち 例えばネットワーク経由で コンテンツを取得するために 非同期コードの導入が必要になるでしょう コードでは UIのハングを避けながら ネットワーク経由でコンテンツが 届くのを待機できます タスクの実行に時間がかかる場合 メインスレッドと同時に実行される バックグラウンドスレッドに タスクを移動できます

    アプリの開発をさらに進める中で すべてのデータをメインスレッド内に 保持するとアプリのパフォーマンスが 低下する場合があります そこで 常にバックグラウンドで実行される 特定の目的のための データ型を導入できます

    Swiftの並行処理は このような種類の 並行動作を表現するための アクターやタスクなどの ツールを提供します 大規模なアプリは このような アーキテクチャを含む可能性が高いですが 最初から含める必要はなく すべてのアプリに必要なわけでもありません このセッションでは アプリをシングルスレッドから 並行処理まで移行する 様々な段階について 順を追って説明します 各段階について説明する中で その段階を採用する状況 使用するSwift言語の機能 それらを効果的に使用する方法 それらの仕組みについて説明します まず シングルスレッドコードとSwiftの 並行処理の連携について説明します その後 ネットワークアクセスなど 遅延のある処理を支援する 非同期タスクを紹介します 次に バックグラウンドスレッドに 作業を移動するための並行処理を紹介し データ競合を発生させずにスレッド間で データを共有する方法を説明します 最後に アクターを使用してデータを メインスレッドから移動します 最初に シングルスレッドコードです プログラムを実行すると コードの実行が メインスレッドで開始されます 追加したコードは 並行処理を明示的に導入して 別の場所でコードを実行するまで メインスレッドにとどまります シングルスレッドコードは 一度に1つのことしかしないため コードの記述と保守が容易です 後から並行処理の導入を 始めると Swiftは メインスレッドのコードを保護します

    メインスレッドとそのすべてのデータは メインアクターによって表現されます メインアクターを実行できる メインスレッドは1つしかないため メインアクターに並行処理はありません @MainActor表記によりデータやコードが メインアクター上にあることを指定できます Swiftは メインアクターコードが メインスレッドでのみ実行でき メインアクターのデータは そこからしか アクセスできないことを保証します このようなコードはメインアクターに 隔離されていると言います Swiftは デフォルトでメインアクターを 使用してメインスレッドコードを保護します これは Swiftコンパイラが そのモジュールのすべてについて @MainActorを記述するようなものです これにより コード内のどこからでも 静的変数のように共有状態に 自由にアクセスできます メインアクターモードでは 並行処理の 導入を開始するまで 同時アクセスについて 心配する必要はありません デフォルトではメインアクターを使用した コードの保護はビルド設定に基づきます これは主としてメインアプリモジュールと UIインタラクションに焦点を 当てたモジュールに使用します このモードはXcode 26で作成された 新規アプリプロジェクトにおいて デフォルトで有効です このセッションでは メインアクターモードが コード例全体で有効になっていると 仮定します

    画像モデルにメソッドを追加し URLから画像を取得して表示しましょう ローカルファイルから 画像を読み込みます それをデコードし UIに表示します このアプリには並行処理がありません すべての作業を行う1つの メインスレッドがあるだけです この関数は全体が メインスレッド上で実行されます ここですべての処理が 十分に高速なら問題ありません

    現時点ではローカルでファイルを 読み取ることしかできません アプリがネットワーク経由で 画像を取得できるようにするには 別のAPIを使用する必要があります

    このURLSession APIではURLを指定し ネットワーク経由でデータを取得できます ただし このメソッドを メインスレッドで実行すると データがネットワークからダウンロード されるまでUIがフリーズします デベロッパとして アプリの 応答性を維持することが重要です つまり メインスレッドを長時間拘束 しすぎて UIの誤動作やハングが 起きないよう注意する必要があります Swiftの並行処理には 有用なツールがあります 非同期タスクは ネットワーク要求など データを待機するときに使用でき メインスレッドを拘束しません このようなハングを防ぐために ネットワークアクセスは非同期です fetchAndDisplayImageを変更して 非同期呼び出しを処理できます それには関数を「async」にし 「await」を 付けてURLセッションAPIを呼び出します awaitは関数が一時停止する 場所を示します つまり その関数が待っている イベントが発生するまで 現在のスレッドの実行が停止します その後で実行が再開されます

    これは関数を2つの部分に分割する ことと考えることができます つまり 画像の取得を開始する 前に実行される部分と 画像の取得後に実行される部分です このように関数を分割することで UIの応答性を維持しながら 2つの部分の間で 他の作業を実行できます

    実際 URLSessionなど 多くのライブラリAPIは 作業をバックグラウンドに オフロードします このコードにはまだ並行処理を 導入していません その必要がなかったからです 私たちはアプリの一部を非同期にし 私たちに代わって作業をオフロードする ライブラリAPIを呼び出すことで 応答性を向上させました 行う必要があったのは コードに asyncとawaitを採用することだけです

    今のところ このコードは1つの 非同期関数を実行しているだけです 非同期関数はタスクで実行されます タスクは他のコードとは独立で実行され 特定の処理をエンドツーエンドで 実行するように作成する必要があります ボタン押下などのイベントに応答して タスクを作成するのが一般的です ここで タスクは画像の取得から 表示までの処理をすべて実行します 1つのアプリに多数の非同期タスクが 存在する可能性があります ここまで説明してきた 画像の 取得から表示までのタスクに加えて ここでは 2番目のタスクとして ニュースを取得してそれを表示し 更新を待つタスクを追加しました 各タスクは最初から最後まで 順番に処理を完了します 取得はバックグラウンドで行われますが 各タスクの他の処理は すべてメインスレッドで実行され 処理は一度に1つだけ実行されます タスクは互いに独立しているため 各タスクはメインスレッドで 交代で実行できます メインスレッドは 各タスクの部分を 実行する準備ができると それらの部分を実行します 複数のタスクを交代で実行するシングル スレッドは「インターリーブ」と呼ばれます これはシステムリソースを最も効率的に 使用し全体的なパフォーマンスを高めます 1つの処理を待つ間 スレッドを アイドル状態にするのではなく 各タスクの進行を できるだけ早く開始できます ニュースを取得する前に 画像の取得が完了した場合は メインスレッドは ニュースを表示する前に 画像のデコードと表示を開始します しかし ニュースの取得が 先に終わった場合 メインスレッドは画像をデコードする前に ニュースの表示を開始できます

    複数の非同期タスクは 独立した 多数の処理をアプリで同時に 実行する必要がある場合に最適です 特定の順序で作業を行う必要が ある場合は その作業を 1つのタスクで実行する必要があります

    ネットワーク要求など 遅延のある処理がある場合に アプリの応答性を高めるには 非同期タスクを使用して その遅延を減らします ここでライブラリが役に立ちます 独自のコードはメインスレッドに 維持したまま 並行処理を行う 非同期APIが用意されています URLSession APIは ある種の 並行処理をすでに導入しており ネットワークアクセスを バックグラウンドスレッドで処理します 画像の取得から表示までの独自の処理は メインスレッドで実行されます 場合によっては デコード処理に時間が かかりすぎることに気付きます これは 大きな画像のデコード中にUIが ハングして明らかになることがあります

    多くの場合 アプリでは非同期の シングルスレッドで十分です ただし アプリの応答性が 低いことに気付き始めたら それはメインスレッドでの処理が 多すぎることを示しています Instrumentsなどの プロファイリングツールは どこで多くの処理時間がかかって いるかを判断するのに役立ちます 並行処理なしでその作業を高速化 できる場合は 最初にそれを行います 高速化できない場合は 並行処理の 導入が必要な可能性があります 並行処理は メインスレッドと並行して バックグラウンドスレッドで コードの一部を実行するもので UIをブロックしません また システムで より多くのCPUコアを使用して 作業を高速化するためにも使用できます 目標はメインスレッドから デコードを除外して その作業をバックグラウンド スレッドで行うことです デフォルトモードで メインアクターを使用しているので fetchAndDisplaylmageとdecodelmageは どちらもメインアクターに隔離されます メインアクターコードは メインスレッドに のみアクセス可能なコードと すべてのデータに自由にアクセスできます メインスレッドには並行処理が ないため これは安全です

    ここでは decodeImageの 呼び出しをオフロードします これは@concurrent属性を 関数decodeImageに 適用することで実行できます @concurrentはSwiftにバックグラウンドで 関数を実行するように伝えます decodeImageを実行する場所を変えると decodeImageがアクセスできる 状態に関する想定も変更されます 実装を見てみましょう この実装はメインアクターに格納されている キャッシュされた画像データの辞書を チェックしています これはメイン スレッドでのみ安全に実行できます Swiftコンパイラは 関数が メインアクターのデータに アクセスを試みる場所を表示します これはまさに 並行処理を追加する際に バグを発生させないために 知る必要があることです 並行処理を安全に導入できるように メインアクターから切り離す際に 使用できるいくつかの戦略があります 場合によっては 常にメインアクターで 実行される呼び出し元に メインアクターコードを移動できます これは作業を確実に同期して 実行する場合に適切な戦略です またはawaitを使用して並行処理コードから メインアクターに非同期アクセスします

    コードをメインアクターで実行する 必要がまったくない場合は nonisolatedキーワードを追加して 任意のアクターから分離できます まず 1つ目の戦略について説明します 他の戦略については後で説明します 画像キャッシュを メインアクターで実行される fetchAndDisplayImageに移動します 非同期呼び出しを行う前にキャッシュを 確認すると遅延の排除に有効です 画像がキャッシュにある場合 fetchAndDisplayImageは 一時停止することなく 同期的に完了します つまり 結果はすぐにUIに提供され 画像をまだ利用できない 場合にのみ一時停止します

    さらに urlパラメータは 不要になったので decodeImageから削除できます これで decodeImageの 結果を待つだけになります

    @concurrent関数は 実行する アクターを常にオフにします 呼び出し元のアクターを問わず 関数をオンのままにする場合は nonisolatedキーワードを使用できます Swiftには より多くの並行処理を 導入するための別の方法があります 詳細については「Beyond the basics of structured concurrency」をご覧ください

    当社がデコードAPIを多くのクライアントが 使用できるライブラリの一部として 提供していたら @concurrentの使用は 必ずしも最適ではありません データのデコードにかかる時間は データの大きさによって異なり 少量のデータのデコードは メインスレッドで実行して問題ありません ライブラリについては nonisolated APIを提供し クライアントに作業をオフロードするかの 判断を任せるのが最善です

    nonisolatedコードはどこからでも 呼び出すことができるため非常に柔軟です メインアクターから呼び出すと メインアクターにとどまります バックグラウンドスレッドから呼び出すと バックグラウンドスレッドにとどまります 汎用ライブラリの場合 これは優れたデフォルト機能です 作業をバックグラウンドにオフロードすると システムがバックグラウンドスレッドで 実行する作業の スケジュールを設定します 並行処理スレッドプールにはシステムの バックグラウンドスレッドのすべてが含まれ 任意の数のスレッドが 含まれる可能性があります Watchなどの小型デバイスの場合 プールにスレッドが1つまたは 2つしかない場合があります コア数が多い大きなシステムではプール内の バックグラウンドスレッド数が多くなります タスクがどのバックグラウンドスレッドで 実行されるかは問題ではなく リソースの効率的な利用を システムに任せることができます 例えば タスクが一時停止すると 元のスレッドは準備ができている 他のタスクの実行を開始します タスクが再開されると 並行処理プール内の使用可能な スレッドで実行を開始できます そのスレッドは開始時のバックグラウンド スレッドとは異なる場合があります

    並行処理を準備できたので 次に 異なる スレッド間でデータを共有しましょう 並行処理コードでの変更可能な状態の共有は 修正困難な実行時バグを招く間違いを 起こしやすいことで有名です Swiftはコンパイル時にこれらの 間違いの検出を支援するため 安心して並行処理コードを記述できます メインアクターと並行処理プールの 間を行き来するたびに 異なるスレッド間でデータが共有されます UIからURLを取得するとメインアクターから バックグラウンドスレッドに そのURLが渡され画像が取得されます 画像が取得されるとデータが返り 画像デコードに渡されます 画像がデコードされた後 selfを指定して画像が メインアクターに戻されます Swiftにより 並行処理コード内でこれらの 値すべての安全なアクセスが確保されます UIの更新によってURLを含む 追加のタスクが作成されると どうなるか見てみましょう 幸いURLは値型です つまり URLをバックグラウンド スレッドにコピーすると メインスレッドのものとは別のコピーが バックグラウンドスレッドに作られます ユーザーがUIから新しい URLを入力した場合 メインスレッドのコードは そのコピーを自由に使用または変更でき 変更はバックグラウンドスレッドで 使用する値には影響を与えません つまり URLなどの値型は 共有しても安全です 結局のところ 実際には共有されておらず 各コピーは互いに独立しているからです

    値型は当初からSwiftの 大きな部分を占めてきました 文字列 整数 日付などの 基本的な型はすべて値型です

    辞書や配列など 値型のコレクションも値型です このPost構造体のように値型を格納する 構造体と列挙型も同様です 同時に共有しても常に安全な型を Sendable型と呼びます Sendableはプロトコルであり Sendableに 適合する型は安全に共有できます ArrayなどのコレクションはSendableへの 条件付き適合を定義するため 要素がSendableの場合 それらはSendableです 構造体と列挙型は すべてのインスタンスデータが Sendableの場合にSendable としてマークできます メインアクター型は 暗黙的にSendableであり 明示的に宣言する必要はありません メインアクターなどのアクターは 一度に1つのタスクのみが アクセスできるようにすることで 非Sendableな状態を保護します アクターは渡された値を メソッドに格納する場合があります またアクターは そのメソッドから保護 された状態への参照を返す場合があります 値がアクターとやり取りされる場合 Swiftコンパイラは 値を並行処理コードに安全に渡すことが できることを必ず確認します decodeImageのasync呼び出しに 焦点を当てましょう

    decodeImageはインスタンスメソッドなので 暗黙的なself引数を渡しています

    ここでは2つの値がメイン アクター外に渡されて 結果の値がメインアクターに 戻されることがわかります 「self」は私のイメージモデルクラスであり メインアクターに隔離されています メインアクターは変更可能な状態を 保護するため クラスへの参照を バックグラウンドスレッドに 安全に渡すことができます dataは値型であるためSendableです

    残りは画像の型です dataのように値型である可能性があり その場合はSendableです 代わりに クラスなどのSendable ではない型について考えます クラスは参照型であり ある変数を 別の変数に代入すると それらは メモリ内の同じオブジェクトを指します 1つの変数を介して画像の 拡大縮小などのように オブジェクトについて何かを 変更すると それらの変更は 同じオブジェクトを指す 他の変数に即座に反映されます fetchAndDisplayImageは imageの値を同時に使用しません decodeImageはバックグラウンドで実行され アクターによって保護されている 状態にはアクセスできません decodeImageは指定されたデータから 画像の新しいインスタンスを作成します この画像は並行処理コードで 参照できないため メインアクターに渡し UIに表示しても安全です 並行処理を導入すると どうなるか見てみましょう 最初に このscaleAndDisplayメソッドは メインスレッドに新しい画像を読み込みます image変数は猫の画像を含むこの 画像オブジェクトを指します さらに この関数は並行処理プールで 実行するタスクを作成します このタスクは画像のコピーを取得します 最後にメインスレッドで画像を表示します ここで問題が発生します バックグラウンドスレッドでは画像を 変更しています 幅と高さを変え ピクセルを拡大縮小された バージョンに置き換えています それと同時にメインスレッドでは 前の幅と高さに基づく ピクセルを使用しています これはデータ競合です UIの不具合が発生するか プログラムがピクセル配列の境界外に アクセスしようとしたときに クラッシュする可能性が高くなります Swiftの並行処理では データ競合を防ぐために コードが非Sendable型を共有しようと するとコンパイラエラーが発生します ここでコンパイラは メインアクターで 表示するために使用される画像を 並行処理タスクが取得している ことを示しています これを修正するには 同じオブジェクトを 平行して共有するのを 確実に避ける必要があります 画像の効果をUIに表示する場合 画像の拡大縮小が 完了するのを待ってから 画像を表示するのが 適切な解決策です これら3つの処理をすべて タスクに移動することで 確実にそれらを順に実行できます displayImageはメインアクターで 実行する必要があるので awaitを使用して並行処理 タスクから呼び出します scaleAndDisplayを直接 非同期にすることができれば コードをシンプルにして 新しいタスクを作成せずに scaleAndDisplayを呼び出すタスク内で 3つの処理を順番に実行できます UIに表示する画像を メインアクターに渡したら メインアクターは画像オブジェクトを キャッシュするなどして 画像への参照を自由に保存できます 画像がUIに表示された後に 画像を変更しようとすると 安全でない同時アクセスに関する コンパイラエラーが発生します 画像をメインアクターに渡す前に 画像に変更を加えることで この問題に対処できます データモデルにクラスを 使用している場合 モデルクラスはおそらく メインアクターで開始されるため クラスから作成した画像を UIに表示できます 最終的にバックグラウンドスレッドで 作業を行う必要があると判断した場合は クラスをnonisolatedにします ただし Sendableには しないほうがよいでしょう 皆さんはモデルの一部が メインスレッドで更新され モデルの他の部分が バックグラウンドスレッドで 更新される状況を望まないと思います モデルクラスを非Sendableに維持すると この種の同時変更が発生するのを 防ぐことができます また 簡単でもあります なぜならクラスをSendableにするには ロックなどの低レベル同期メカニズムを 通常使用する必要があるからです クラスと同様にクロージャで 共有状態を作成できます これは 画像を拡大縮小して表示する 先ほどの関数と同様の関数です 画像オブジェクトを作成します 次に perform(afterDelay:)を呼び出し 画像オブジェクトを拡大縮小する クロージャに画像を提供します このクロージャには同じ画像への 別の参照が含まれています これをimage変数のキャプチャと呼びます 非Sendableクラスと同様に 共有状態のクロージャは同時に 呼び出されない限り安全です 関数タイプをSendableにするのは 同時に共有する必要がある場合のみです

    Sendableのチェックは アクターとタスクの 間でデータが渡されるたびに発生します これは アプリのバグの原因になる データ競合を 発生させないためです 多くの一般的な型はSendableであり それらは並行処理タスク間で 自由に共有できます クラスとクロージャは 同時に 共有するのが安全ではない変更可能な 状態を含む可能性があるため 一度に1つのタスクから使用してください

    あるタスクから別のタスクに オブジェクトを渡すことはできますが オブジェクトにすべての変更を加えてから オブジェクトを渡してください 非同期タスクをバックグラウンド スレッドに移動すると メインスレッドを解放して アプリの応答性を維持できます メインアクターに多くの データがあることが原因で それらの非同期タスクがメインスレッドに 頻繁にアクセスすることがわかった場合 アクターを導入するとよいでしょう

    時間の経過とともにアプリの規模が 大きくなるにつれてメインアクターの 状態の量も増える可能性があります ネットワークアクセスの管理などを扱う 新しいサブシステムを導入することでしょう これによりメインアクターに 多くの状態が生じる可能性があります 例えば これにはネットワークマネージャで 処理される一連のオープンな接続があり ネットワーク経由でデータを取得する 必要があるときにアクセスされます これらの追加サブシステムの 使用を開始すると 先ほどの画像の取得から表示までの タスクはより複雑になります バックグラウンドスレッドでの 実行が試みられますが ネットワークマネージャの データが存在する メインスレッドに移動する 必要があります これは競合の原因になります つまり 多数のタスクがメインアクターで 同時にコードを実行しようと試みます 個々の処理は短時間かもしれませんが 多数のタスクがこれを行う場合 UIの不具合を招く可能性があります 前にコードを@concurrent関数に 入れることによって メインスレッドから コードを移動しました ここで すべての作業はネットワーク マネージャのデータへのアクセスが必要です これを移動するために独自のネットワーク マネージャアクターを導入できます メインアクターと同様に アクターはデータを隔離するので そのアクターでの実行中にのみ そのデータにアクセスできます メインアクターとともに独自の アクター型を定義できます アクター型はメインアクター クラスに似ています メインアクタークラスのように データを隔離するので 一度に1つのスレッドのみが データにアクセスできます アクター型もSendableなのでアクター オブジェクトを自由に共有できます メインアクターとは異なり それぞれが 独立した多数のアクターオブジェクトが プログラムに存在する可能性があります また アクターオブジェクトは1つの スレッドに関連付けられていません そのため いくつかの状態をメインアクター からアクターオブジェクトに移動すると バックグラウンドスレッドでより多くの コードを実行できるようになり メインスレッドに空きが作られ UIの応答性が維持されます

    メインアクターにデータを格納すると メインスレッドで実行するコードが 多くなりすぎる場合に アクターを使用してください その際にネットワーク管理コードなど UI以外の部分に該当するコードの データを新しいアクターに移動します

    アプリのほとんどのクラスはおそらく アクターとしての使用を意図していません UI向けのクラスはUIの状態と 直接やり取りできるように メインアクターに残すべきです モデルクラスは通常 UIとともに メインアクターに置くか 非Sendableに保つべきです それにより モデルへの多数の 同時アクセスを抑制できます ここでは シングルスレッド コードから説明を始めました 必要性の高まりに応じて 遅延を減らす非同期タスク バックグラウンドスレッドで 実行する並行処理コード データアクセスをメインスレッドから 移動するアクターを導入しました 時間とともに 多くのアプリは これと同じ経過をたどります

    プロファイリングツールでメインスレッド からいつどのコードを移すか特定できます Swiftの並行処理はコードをメイン スレッドから正しく分離するのに役立ち アプリのパフォーマンスと 応答性を向上させます

    並行処理の導入に役立つ アプリの推奨ビルド設定が いくつかあります アプローチしやすい並行処理設定は 並行処理の扱いを容易にする 今後提供される一連の機能を有効にします すべてのプロジェクトでこの設定を 採用することをおすすめします メインアプリモジュールなど 主にUIとやり取りする Swiftモジュールの場合 デフォルトのアクター隔離を「メイン アクター」に設定することもおすすめします これにより 明示的に設定しない限り メインアクターにコードを配置できます これらの設定を連携させることで より 簡単にシングルスレッドアプリを記述でき 必要なときに より親しみやすい方法で 並行処理を導入できます Swiftの並行処理はアプリの改善に 役立つように設計されたツールです アプリにパフォーマンスの 問題がある場合に 非同期や並行処理コードの導入に 使用してください Swift 6移行ガイドはデータ競合を避ける 方法や並行処理に関する様々な疑問の 答えを見つけるのに役立ちます また このセッションの概念を 適用したサンプルアプリについては Code Alongの関連セッションを ご覧ください ありがとうございます

    • 3:20 - Single-threaded program

      var greeting = "Hello, World!"
      
      func readArguments() { }
      
      func greet() {
        print(greeting)
      }
      
      readArguments()
      greet()
    • 4:13 - Data types in a the app

      struct Image {
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
      }
      
      final class Library {
        static let shared: Library = Library()
      }
    • 4:57 - Load and display a local image

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) throws {
          let data = try Data(contentsOf: url)
          let image = decodeImage(data)
          view.displayImage(image)
        }
      
        func decodeImage(_ data: Data) -> Image {
          Image()
        }
      }
      
      final class Library {
        static let shared: Library = Library()
      }
    • 5:36 - Fetch and display an image over the network

      import Foundation
      
      struct Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) throws {
          let (data, _) = try URLSession.shared.data(from: url)
          let image = decodeImage(data)
          view.displayImage(image)
        }
      
        func decodeImage(_ data: Data) -> Image {
          Image()
        }
      }
      
      final class Library {
        static let shared: Library = Library()
      }
    • 6:10 - Fetch and display image over the network asynchronously

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = decodeImage(data)
          view.displayImage(image)
        }
      
        func decodeImage(_ data: Data) -> Image {
          Image()
        }
      }
      
      final class Library {
        static let shared: Library = Library()
      }
    • 7:31 - Creating a task to perform asynchronous work

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
        var url: URL = URL("https://swift.org")!
      
        func onTapEvent() {
          Task {
            do {
      	try await fetchAndDisplayImage(url: url)
            } catch let error {
              displayError(error)
            }
          }
        }
      
        func displayError(_ error: any Error) {
        }
      
        func fetchAndDisplayImage(url: URL) async throws {
        }
      }
      
      final class Library {
        static let shared: Library = Library()
      }
    • 9:15 - Ordered operations in a task

      import Foundation
      
      class Image {
        func applyImageEffect() async { }
      }
      
      final class ImageModel {
        func displayImage(_ image: Image) {
        }
      
        func loadImage() async -> Image {
          Image()
        }
        
        func onButtonTap() {
          Task {
            let image = await loadImage()
            await image.applyImageEffect()
            displayImage(image)
          }
        }
      }
    • 9:38 - Fetch and display image over the network asynchronously

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = decodeImage(data)
          view.displayImage(image)
        }
      
        func decodeImage(_ data: Data) -> Image {
          Image()
        }
      }
    • 10:40 - Fetch and display image over the network asynchronously

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = decodeImage(data, at: url)
          view.displayImage(image)
        }
      
        func decodeImage(_ data: Data, at url: URL) -> Image {
          Image()
        }
      }
    • 11:11 - Fetch over network asynchronously and decode concurrently

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = await decodeImage(data, at: url)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data, at url: URL) async -> Image {
          Image()
        }
      }
    • 11:30 - Implementation of decodeImage

      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = await decodeImage(data, at: url)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data, at url: URL) async -> Image {
          if let image = cachedImage[url] {
            return image
          }
      
          // decode image
          let image = Image()
          cachedImage[url] = image
          return image
        }
      }
    • 12:37 - Correct implementation of fetchAndDisplayImage with caching and concurrency

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          if let image = cachedImage[url] {
            view.displayImage(image)
            return
          }
      
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = await decodeImage(data)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data) async -> Image {
          // decode image
          Image()
        }
      }
    • 13:30 - JSONDecoder API should be non isolated

      // Foundation
      import Foundation
      
      nonisolated
      public class JSONDecoder {
        public func decode<T: Decodable>(_ type: T.Type, from data: Data) -> T {
          fatalError("not implemented")
        }
      }
    • 15:18 - Fetch over network asynchronously and decode concurrently

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = await decodeImage(data, at: url)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data, at url: URL) async -> Image {
          Image()
        }
      }
    • 16:30 - Example of value types

      // Value types are common in Swift
      import Foundation
      
      struct Post {
        var author: String
        var title: String
        var date: Date
        var categories: [String]
      }
    • 16:56 - Sendable value types

      import Foundation
      
      // Value types are Sendable
      extension URL: Sendable {}
      
      // Collections of Sendable elements
      extension Array: Sendable where Element: Sendable {}
      
      // Structs and enums with Sendable storage
      struct ImageRequest: Sendable {
        var url: URL
      }
      
      // Main-actor types are implicitly Sendable
      @MainActor class ImageModel {}
    • 17:25 - Fetch over network asynchronously and decode concurrently

      import Foundation
      
      class Image {
      }
      
      final class View {
        func displayImage(_ image: Image) {
        }
      }
      
      final class ImageModel {
        var imageCache: [URL: Image] = [:]
        let view = View()
      
        func fetchAndDisplayImage(url: URL) async throws {
          let (data, _) = try await URLSession.shared.data(from: url)
          let image = await self.decodeImage(data, at: url)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data, at url: URL) async -> Image {
          Image()
        }
      }
    • 18:34 - MyImage class with reference semantics

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scale(by factor: Double) {
        }
      }
      
      let image = MyImage()
      let otherImage = image // refers to the same object as 'image'
      image.scale(by: 0.5)   // also changes otherImage!
    • 19:19 - Concurrently scaling while displaying an image is a data race

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scaleImage(by factor: Double) {
        }
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
      
        // Slide content start
        func scaleAndDisplay(imageName: String) {
          let image = loadImage(imageName)
          Task { @concurrent in
            image.scaleImage(by: 0.5)
          }
      
          view.displayImage(image)
        }
        // Slide content end
      
        func loadImage(_ imageName: String) -> MyImage {
          // decode image
          return MyImage()
        }
      }
    • 20:38 - Scaling and then displaying an image eliminates the data race

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scaleImage(by factor: Double) {
        }
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
      
        func scaleAndDisplay(imageName: String) {
          Task { @concurrent in
            let image = loadImage(imageName)
            image.scaleImage(by: 0.5)
            await view.displayImage(image)
          }
        }
      
        nonisolated
        func loadImage(_ imageName: String) -> MyImage {
          // decode image
          return MyImage()
        }
      }
    • 20:54 - Scaling and then displaying an image within a concurrent asynchronous function

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scaleImage(by factor: Double) {
        }
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
      
        @concurrent
        func scaleAndDisplay(imageName: String) async {
          let image = loadImage(imageName)
          image.scaleImage(by: 0.5)
          await view.displayImage(image)
        }
      
        nonisolated
        func loadImage(_ imageName: String) -> MyImage {
          // decode image
          return MyImage()
        }
      }
    • 21:11 - Scaling, then displaying and concurrently modifying an image is a data race

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scaleImage(by factor: Double) {
        }
      
        func applyAnotherEffect() {
        }
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
      
        // Slide content start
        @concurrent
        func scaleAndDisplay(imageName: String) async {
          let image = loadImage(imageName)
          image.scaleImage(by: 0.5)
          await view.displayImage(image)
          image.applyAnotherEffect()
        }
        // Slide content end
      
        nonisolated
        func loadImage(_ imageName: String) -> MyImage {
          // decode image
          return MyImage()
        }
      }
    • 21:20 - Applying image transforms before sending to the main actor

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scaleImage(by factor: Double) {
        }
      
        func applyAnotherEffect() {
        }
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
      
        // Slide content start
        @concurrent
        func scaleAndDisplay(imageName: String) async {
          let image = loadImage(imageName)
          image.scaleImage(by: 0.5)
          image.applyAnotherEffect()
          await view.displayImage(image)
        }
        // Slide content end
      
        nonisolated
        func loadImage(_ imageName: String) -> MyImage {
          // decode image
          return MyImage()
        }
      }
    • 22:06 - Closures create shared state

      import Foundation
      
      struct Color { }
      
      nonisolated class MyImage {
        var width: Int
        var height: Int
        var pixels: [Color]
        var url: URL
      
        init() {
          width = 100
          height = 100
          pixels = []
          url = URL("https://swift.org")!
        }
      
        func scale(by factor: Double) {
        }
      
        func applyAnotherEffect() {
        }
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
      
        // Slide content start
        @concurrent
        func scaleAndDisplay(imageName: String) async throws {
          let image = loadImage(imageName)
          try await perform(afterDelay: 0.1) {
            image.scale(by: 0.5)
          }
          await view.displayImage(image)
        }
      
        nonisolated
        func perform(afterDelay delay: Double, body: () -> Void) async throws {
          try await Task.sleep(for: .seconds(delay))
          body()
        }
        // Slide content end
        
        nonisolated
        func loadImage(_ imageName: String) -> MyImage {
          // decode image
          return MyImage()
        }
      }pet.
    • 23:47 - Network manager class

      import Foundation
      
      nonisolated class MyImage { }
      
      struct Connection {
        func data(from url: URL) async throws -> Data { Data() }
      }
      
      final class NetworkManager {
        var openConnections: [URL: Connection] = [:]
      
        func openConnection(for url: URL) async -> Connection {
          if let connection = openConnections[url] {
            return connection
          }
      
          let connection = Connection()
          openConnections[url] = connection
          return connection
        }
      
        func closeConnection(_ connection: Connection, for url: URL) async {
          openConnections.removeValue(forKey: url)
        }
      
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
        let networkManager: NetworkManager = NetworkManager()
      
        func fetchAndDisplayImage(url: URL) async throws {
          if let image = cachedImage[url] {
            view.displayImage(image)
            return
          }
      
          let connection = await networkManager.openConnection(for: url)
          let data = try await connection.data(from: url)
          await networkManager.closeConnection(connection, for: url)
      
          let image = await decodeImage(data)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data) async -> MyImage {
          // decode image
          return MyImage()
        }
      }
    • 25:10 - Network manager as an actor

      import Foundation
      
      nonisolated class MyImage { }
      
      struct Connection {
        func data(from url: URL) async throws -> Data { Data() }
      }
      
      actor NetworkManager {
        var openConnections: [URL: Connection] = [:]
      
        func openConnection(for url: URL) async -> Connection {
          if let connection = openConnections[url] {
            return connection
          }
      
          let connection = Connection()
          openConnections[url] = connection
          return connection
        }
      
        func closeConnection(_ connection: Connection, for url: URL) async {
          openConnections.removeValue(forKey: url)
        }
      
      }
      
      final class View {
        func displayImage(_ image: MyImage) {
        }
      }
      
      final class ImageModel {
        var cachedImage: [URL: MyImage] = [:]
        let view = View()
        let networkManager: NetworkManager = NetworkManager()
      
        func fetchAndDisplayImage(url: URL) async throws {
          if let image = cachedImage[url] {
            view.displayImage(image)
            return
          }
      
          let connection = await networkManager.openConnection(for: url)
          let data = try await connection.data(from: url)
          await networkManager.closeConnection(connection, for: url)
      
          let image = await decodeImage(data)
          view.displayImage(image)
        }
      
        @concurrent
        func decodeImage(_ data: Data) async -> MyImage {
          // decode image
          return MyImage()
        }
      }

Developer Footer

  • ビデオ
  • WWDC25
  • Swiftの並行処理の活用
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン