ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Background Assetsについて
Background Assetsフレームワークを使用して、CDNから大きなファイルを直接ダウンロードし、Appやゲームの初回起動時のエクスペリエンスを向上させる方法をご確認ください。Appの初回インストール時、Appの更新時、ユーザがAppを使用しているときに定期的にバックグラウンドのダウンロードをスケジュールする方法を紹介します。また、スケジュールされたダウンロードを管理して、ユーザが必要なときに必要なコンテンツを確実に入手する方法についても解説します。
リソース
関連ビデオ
Tech Talks
WWDC23
WWDC21
-
ダウンロード
♪ 落ち着いた雰囲気の ヒップホップ音楽 ♪ ♪ こんにちは Jaredです Appleのソフトウェア エンジニアです 本日は 今年発表となった iOS iPadOS macOSにおける 新しいフレームワークに ついて お話しします Background Assetsと 呼ばれ よく知っているAppや みなさんさんが開発されている Appのユーザー体験を 著しく充実させると 信じております 最初に新しい Background Assets フレームワークをご紹介し その後でこの フレームワークをAppに 採用する方法をお見せします その後 Extensionの概要と それが提供する 新しい機能について 軽くご説明します 最後にベストプラクティスと 本日学んだことを おさらいします スタートする前に 私たちがここで 何を解決しようと しているかを説明します 待つことというのは 面白いものではありません 私たちのソフトウェアを 使われる方々に 待てと言うと 不満が募り Appが提供する 体験が損なわれます 例えば 完璧なAppを 見つけるのに App Storeをずっと 検索した経験は ありませんか? 完璧そうなのが やっと見つかり 「入手」ボタンを タップします 期待はどんどん募るばかり その後すぐに ネットワーク接続や Appのサイズによって ダウンロード完了まで 待つ必要があることに気づきます 数秒待った後 iPhoneを置き コーヒーでも飲みながら マインドフルネスや 我慢強くなるための 実践本を読みます 数時間経ってiPhoneを取り 使うのを1日中 待っていた完璧なAppを 使おうとワクワクします Appを起動した瞬間に 別のダウンロードが 始まります 困惑してしまいます 1日中iPhoneを さわらなかったのに なぜ まだ 待たされるのでしょうか? Appをインストールした後に このコンテンツを 自動的にダウンロード しないのでしょうか? インターネット接続が 遅い場合 イライラしてAppを 消去してしまうでしょう 誰もこんな経験を したくないのが 本音ですよね これは みなさんのせいではなく これをより優れた体験に できることは間違いありません そこで自信を持って 今年ご紹介するのが Background Assetsです このフレームワークは Appのユーザー体験を 充実させるために 開発されました Appが起動した瞬間に 最高の第一印象を 提供するためです Background Assetsは 既存のワークフローと 柔軟に連携するよう 設計されています 多くのみなさんはすでに 複雑な アセット管理システムを 構築しています 私たちはこの フレームワークが すでに開発された みなさんのソリューションに 適合するよう また App Storeへの追加の 提出を行わずに Appに 更新されたコンテンツを プッシュできることを 望んでいます ゲームやその他のAppが 発売された後に 追加のコンテンツが必要に なるのはよくあります アートテクスチャの更新や ゲームレベルのバグ修正のように Background Assetsでは Appのライフサイクルの外で アセットのスケジューリングと 更新が実行可能になります 最初にAppが起動される前や Appが夜中に更新された後に アセットが存在することが 重要であると 私たちは考えています そのためAppの起動時に コンテンツが 利用可能になるよう 仕組みを作成しました さらにフレームワークの 採用がより簡単になると みなさんがAppで使うことを 推奨しやすくなります 大量のアセットが事前に ダウンロードされる 必要があるあらゆる場所で 使用していただけます そうすればAppの待機時間を 短縮し コンテンツの進捗バーを 表示することができます このフレームワークが どのようにこの問題を 解決してくれるのか?と 不思議に思うかもしれません できる限り 拡張可能にするために バックグラウンドでの コンテンツダウンロード向け App Extensionを作成しました この新しいExtensionは 当社の プラットフォームの 別のExtensionが使用する 強力なApp Extensionの仕組みの 上に構築されています これによりAppの ライフサイクルの外で コードを 実行できるようになります 例えば このExtensionは 初めてAppをインストールし そのAppが初回起動される前の 段階で実行されます またAppが バックグラウンドで 自動更新された場合にも 実行されます これにより Appが更新され ユーザがAppを開く前に コンテンツをスケジューリングし ダウンロードすることが 確実に可能になります 最後にExtensionは定期的に バックグラウンドでも実行され 更新されたアセットを確認し 定期的に スケジューリング することができます ここで注意が必要なのは Extensionのランタイムは 長くないということです そのためExtensionで スケジューリングする すべての作業を 迅速にこなす必要があります ダウンロードがすばやく スケジューリングされないと システムがこのExtensionを 終了させてしまいます 他にも注意事項が あります このExtensionを 定期的に実行する頻度は Appの使用頻度によって コントロールされます Appが頻繁に 使用されていない場合 このExtensionのランタイムの 頻度が落ちる形となります 以上が Background Assetsの概要です Appが起動した時に アセットが 利用可能である状態を 確実なものにします これはAppがインストール または更新され ユーザがそのAppを 起動する前に 実行されるExtensionによって 行われます ではプロジェクトに このフレームワークを 採用し 使い始める方法を ご説明します フレームワーク内の Download Managerは Background Assets システムサービスで コミュニケーションを 行うための主な手段となります このManagerは シングルトンオブジェクトで App全体で使用できます Managerを使用して アセットをフォアグラウンド またはバックグラウンドでの ダウンロードを予約します Appが起動する前に 開始した可能性がある 現在進行中のダウンロードの 取得することもできます ダウンロードのキャンセルも 可能です すでに予約されているか ダウンロードの最中で 最初にリクエストした アセットが必要で なくなった場合に便利です AppとExtensionの間の 排他アクセスを 管理するための 同期メカニズムも 導入しました ExtensionとAppによる 同時予約や 既存のダウンロードの 変更を防ぐためです お見せしたい例がありますが それはもう少し後で Background Assetsの 使用がいかに簡単かを お見せしましょう APIのいくつかの基本を 段階的に説明します それからこれをすべて 折り込み App Extensionにまとめる 方法を説明します まず Background Assets フレームワークモジュールを インポートします 次はリモートアセットのある 場所を示す URLを定義します 簡単ですね 次にExtensionと Appの両方が属する Appグループコンテナを 定義します 同じグループに AppとExtensionがあれば ダウンロード中と完了後の アセットを 管理できるようになります Appグループについて よく知らない場合は Xcode 14 の Signing & Capabilityセクションで 簡単に追加できます これは2つ以上のAppが 同じリソース この場合AppとExtensionに アクセスできるようにする 強力な機能です 次にdownload オブジェクトを作成 Background Assets フレームワークは 複数のオブジェクトを サポートします しかし この例では 最も一般的なBAURLDownloadに フォーカスします イニシャライザですぐにURLと Appグループ識別子を 取り入れたのがわかります この情報はシステムに ダウンロードの内容と 結果のファイルが どこに保管されるかを 伝えます また識別子を取り入れ この識別子を使って AppとExtension内で起動する ダウンロードを追跡します エンジンは同じ識別子から 1件のダウンロードのみを 許可します そのため識別子を 一意にしてください 次に BADownloaderManager 共有オブジェクトの参照を 保持します Download Managerは Background Assetsの 単一の インターフェイスです これで ダウンロードの監視 キャンセル 予約ができます 次に弱い参照を BADownloadManagerDelegate プロトコルに適合する デリゲートに パスします このプロトコルについては この後すぐにお話しします ここで重要なのは これが予約された ダウンロードに関する メッセージを受信する点です 後はDownload Managerに ダウンロードの予約を リクエストするだけです 何らかの理由で スケジューリングできない場合は エラーが表示されます バックグラウンドでの スケジューリングに加え フォアグラウンド用の APIも提供しています フォアグラウンドでの 実行は 優先度を 上げるだけでなく 即座にダウンロードを 有効にすることも可能で これはURLSession内の デフォルトセッション 構成と似ています このAPIは Appが Extensionにより予約された バックグラウンドの ダウンロードを フォアグラウンドに 昇格させるために作成しました ここでご注意ください Extension内からの フォアグラウンドの ダウンロードはできません Appからのみ 初期化できます ExtensionはUIを提示せず ユーザはその実行に 気づかないため Extensionでは バックグラウンドでの スケジューリングのみが可能です Appが既存のバックグラウンド ダウンロードを フォアグラウンドに 昇格させたい場合は マネージャから現在 アクティブなダウンロードの リストをフェッチすることで 簡単に実現します 返されたリストには 現在スケジューリングされている ダウンロードが記載され それにはスケジューラにある 進行中や待機中のものも 含まれる場合があります 次にAppは startForegroundDownloadを 呼び出して昇格するプロセスを 開始します ダウンロードがすでに フォアグラウンドにいる場合 このメソッドの呼び出しは 何も効果がありません しかしダウンロードが バックグラウンドにいると 最初に停止されこの時点まで ダウンロードされた コンテンツを再び ダウンロードする必要なく フォアグラウンドで再開します これはBackground Assetsを 使って バックグラウンドで 予約されたダウンロードを いかに簡単に フォアグラウンドに 昇格できるかを シンプルで 効果的に示してくれます 実にシンプルです Download Managerは ダウンロードをスケジューリング 管理するための プライマリな インターフェイスです これらのダウンロード オブジェクトが処理されると デリゲートオブジェクトが メッセージを受信します ではデリゲートについて 説明します デリゲートはExtensionや Appによりスケジュールされた すべてのダウンロードの メッセージを 受信します 複数のダウンロードが スケジューリングされている場合 コールバックがそのすべてを 受信します ここでダウンロード オブジェクトの 一意の識別子を使い それらを区別します Appはデリゲートが BADownloadManagerで 設定された瞬間に コールバックを受信します システムはコールバックを キューに追加しません Appがデリゲートメソッドの 1つに対応しない場合や デリゲートが 設定されていない場合 Extensionが呼び起され メッセージを処理します デリゲートがApp内の BADownloadManagerで 設定されていない場合 Extensionがメッセージを 受信することを 覚えていてください Appがフォアグラウンドに いて ユーザに 提示されている場合で デリゲートが 設立されている場合 コールバックはAppに 送信され Extensionは呼ばれません ExtensionはAppが デリゲートのコールバックを 処理しない場合のみ 起動します ダウンロードが完了 または失敗し Appがこのメッセージを 処理しない場合 Extensionが起動します Extensionはどんな コールバックにも 呼び起されるわけではなく BADownloadManagerDelegateと BADownloaderExtension プロトコルの間で 共通の インターフェイスを共有している コールバックのみです ダウンロードの成功 または失敗が デリゲートと プロトコル間の共通の インターフェイスの例です App Extensionには 独自の エントリポイントがあり それに呼び起されますが Extensionがその時点で 実行されている場合 BADownloadManagerで デリゲートを設定できます これを行うと AppとExtensionの両方が デリゲートへの重複した メッセージを受信します Extensionはデリゲート メッセージを 処理しません BADownloaderExtensionで 定義された エントリポイントのみで 起動します Download Managerの デリゲートの プロトコルを見てみましょう 最初の機能は ダウンロードの開始時に メッセージを 受信するものです これはデバイスが特定の ダウンロードを予約した時に 追跡するのに役立ちます ダウンロードが停止した 場合も通知を受けます 停止が発生する例として Extensionがバックグラウンドで ダウンロードを開始し Appがフォアグラウンドへの 昇格をリクエストした時です この昇格の間に ダウンロードが 一時停止する間隔があります Download Managerは フォアグラウンドでの ダウンロードの監視を 許可します チャレンジリクエストに答える メカニズムも提供しています これは接続の信頼性の検証や 接続を認証するための 認証情報の提供に役立ちます 最も重要な関数は 失敗または完了した ダウンロードを 処理するものです 失敗した場合は 再度スケジューリングするか 原因を判断する必要が あります 成功した場合は システムは オペレーティングシステムが 管理されている場所に ファイルを保存します デバイスにスペースが 十分にない場合 システムがファイルを 削除してくれます ファイルはシステムが 提供した場所に 保管することを 強くお勧めします 絶対に移動させる必要がある ファイルだけを移動し 後でオリジナルの ファイルを削除しない限り 重複させないでください Downloader Managerの デリゲートプロトコルは AppやExtensionが予約した ダウンロードに関する メッセージを 受信するためのものです Extensionのエントリ ポイントではありません そこで次のトピックです Background Assetsで 最も期待される 機能「Extension」を 見てみましょう Extensionは ユーザが Appを起動させる前に ダウンロードをスケジューリング するのを可能にします これにより アセットが 最小の待機時間で Appでの最高の体験を 提供するために 準備が整っていることを 確実にできます 先述のように新しい App Extensionを導入しました これは既存の プロジェクトの中の Xcode内で作成できます ちなみにExtensionは Appがインストールまたは 更新された時に実行するので Appの変更で 常に 最新のアセットが 揃うことを確実にします またユーザがAppを 使用する頻度に応じて 定期的に実行されます ユーザが毎日Appを使用すると システムはこの行動を記憶し Extensionはより頻繁に 実行されます しかしAppがまったく 起動されない場合 この定期的な確認の頻度は 低下します 新しいExtensionは ライフサイクルが短く アセットのダウンロードに 使用を制限するために サンドボックスも 小さいものとなっています このExtensionでは すばやい決断を行うべきでして 実装もBackground Assets フレームワークに限定すべきです Extensionを詳しく見る前に Extensionを起動させるために いくつかのものを 設定する必要があります これらの変更は Appが App Storeでの提供に 承認されるための 要件でもあります Appの 情報プロパティリストで いくつかの追加のキーを 定義する必要があります これはExtensionのInfo.plistに 配置できません Appのみです 最初のキーは BAInitialDownloadRestrictions これはExtensionに設定する 制限を指定するための ディクショナリです この制限はApp Reviewにより 審査されるので できる限り 正確にしてください 次はディクショナリ内の それぞれのキーを 見てみましょう 最初の制限は DownloadAllowanceです これはバイトで表現され 最初のAppインストール中に Extension内でリクエストする 最大のダウンロードサイズです このサイズはダウンロードを リクエストする すべてのサイズの合計 となります 各ファイルのサイズでは ありません 次はDomainAllowListです これは文字列で表現される ドメインの配列を取り入れます プレフィックス ワイルドカードをサポートし Extensionがダウンロードを 許可されている ホスト名のリストを 取り入れます ここで重要な点は DownloadAllowanceや AllowListなどの BAInitialDownloadRestrictionsの キーはAppの 最初のインストール後にのみ 適用される形となります Appが起動すると これらの制限は 適用されなくなります 最後の必須のキーは Info.plistの ルートにある これらのアセットのための 追加のストレージの Appが必要とする 最大サイズです 圧縮されたアセットを ダウンロードしたい場合 この値は 最後に抽出される 非圧縮のサイズです ここに表示される数字は Appがダウンロード される前に App Storeに 表示されます 管理について お話ししたところで Extensionのエントリポイントの 詳細をお話しします プロトコルから定義した 関数は Appではなくシステムにより 呼び出されます AppがExtensionとの対話に 責任を持つ 他のApp Extensionとは異なり Background Download Extensionは システムが仲介します システムがExtensionの ライフサイクルを維持するため 一時的なサービスとして 見られるべきです プロトコル内の関数が 呼び出された場合 そこで行われる作業を 最小限に抑えることが 重要です これが起動した後 Extensionはすばやく 終了します ここは解凍する場所や 時間のかかる複雑な操作を 行う場所ではありません Extensionで作業する際の 利点の1つは Appで利用可能なすべての BackgroundAssets APIが Extension内でも使えることです ただしForegroundDownload APIは除外されます つまりAppで行うように BADownloadManagerを 使えるということです 実際に AppとExtensionの両方で 同じコードで アセットの予約や管理を 行う何かが作成できることに お気づきになるでしょう また Extensionを作成する際は どちらも共通のAppグループに いる必要があります 同じグループの識別子を使い AppとExtensionが コンテンツを 読み書きできるように します ではExtensionを適合させる Downloader Extension プロトコルを見てみましょう 最初に気づくのは Download Manager デリゲートプロトコルと かなり似ているところです 前述のように BADownloadManagerを使い Extension内からデリゲートを 構築することができます しかしExtensionを呼び出せるのは このエントリポイントのみです 最初の関数はAppが 最初に インストールされた時に 呼び出されます Appは起動していませんが Extensionは起動しています これはAppが起動された時に 最高の体験を提供するために 必要なダウンロードを スケジューリングするのに 最適な機会となります また 最初の Appインストールの間は ダウンロードの制約が 適用されています Info.plistで定義した BADownloadRestrictions キーを確認して 許可されている最大 ダウンロードサイズと ドメインを 把握する必要があります 次の関数はApp Storeが Appを更新した際に 呼び出されます ユーザがAppスイッチャで Appを終了していない限り 新しく更新された Extensionが起動し 作業をスケジューリング することができます checkForUpdates関数は Extensionがシステムにより 定期的に呼び出されるのを サポートするので バックグラウンド ダウンロードの必要がある 更新を知ることができます この関数はユーザが Appを使用する頻度により 呼び出されます 認証チャレンジ リクエストへの 反応のサポートも 利用可能で ダウンロードするファイルが 信頼のおけるソースからの ものに 制限できます 最後にデリゲートと 同様に ダウンロードの失敗や 成功の通知を受けます backgroundDownloadDidFail 関数では エラーが返されません エラーはステートと共に BADownload オブジェクトの変数で 取得できます 最後の3つの関数は Extensionがダウンロードを 予約していない場合でも 呼び出すことができることを 覚えていてください Appがダウンロードを 予約したのに バックグラウンドに なった場合は Extensionが ダウンロードを 実行することになります AppやExtensionで BADownloaderManagerを 使用する方法が わかったところで AppとExtensionが 同時に実行するという 意味を調べてみましょう 例えば システムが Extensionを呼び起こして 定期的に更新を 確認させようとします この確認を行うために Extensionはネットワークに アクセスする必要があり BADownloaderManagerで カタログのダウンロードや 利用可能な更新済みの アセットのリストを提供する 他のメタデータの ダウンロードの スケジューリングをします 例えば ファイルが100KBの 小さなサイズのカタログで それに含まれる大型の 複数ギガバイトのアセットを ダウンロードする必要が あるとします Extensionはスケジューリング したダウンロードが いつ完了したか失敗したかを 知る必要があり Download Managerに デリゲートをアタッチします Download Managerの デリゲートは 小さなファイルを ダウンロードして どの大型のアセットが スケジューリングされるか判断するため Extensionエントリポイントで 使用されます またExtension エントリポイントは 即座に呼び出されるかは 保証されません ダウンロードが 完了すると Extensionはデリゲート 経由でメッセージを受信 これでExtensionはカタログ ファイルにアクセスでき ダウンロードされた ファイルで 何を行うか 決定する必要があります Extensionがファイルを読み カタログの どのアセットがデバイスに ダウンロードされるかを 決定するはずです 次にExtensionはこの 大型のアセットの バックグラウンド ダウンロードを予約します ダウンロードされた ファイルは必要なくなり Extensionはファイルを 削除するはずです これは適切なように 聞こえますが Extensionが実行し 独自の BADownloadManageを 作成している最中に Appが起動したら? では 見てください Appが起動し 更新されたコンテンツが あるかを知りたがります アセットが最新状態かを 判断するために利用できる Appグループに バージョン 番号があるかもしれません Appは最新のカタログが ダウンロードされる前に 起動したため マネージャから 現在のダウンロードを フェッチし カタログのダウンロードは 現在進行中で デリゲート内で完了を 待っていることに 気づきます しかしここで問題です AppとExtensionのどちらも それぞれの デリゲートから ダウンロード完了という メッセージを受け取ります これはデータ競合が 起こったという意味です AppとExtensionの両方は 同時にファイルを読み 削除しようとします 問題です これはAppかExtensionが 存在しないファイルを 読もうとしているからです ここではAppと Extensionを App内の2つの スレッドのように 考える必要があります 幸いにも Background Assetsは AppとExtensionを同期する 方法を提供します ではそれについて 説明しましょう Background Assetsなら AppとExtensionの 同期が非常に簡単です 今表示されているのは ダウンロードが完了した時の Download Maangerの デリゲート関数です AppやExtensionが アクセスできるファイルへの ローカルパスを含む URLが提供されています この例では このファイルの 排他制御を確保します 次にDownload Manager への参照をつかみ completionハンドラを取り入れる withExclusiveControl 関数を使用します すべてのコードは completionハンドラ内で実行され 排他制御を必要とする その他の呼び出しと 共通で排他制御を行います つまりAppが completionハンドラから戻る前に ExtensionがwithExclusiveControlを 呼び出した場合 Extensionは待つという意味です 反対方向でも 同じことが適用されます Extensionが排他制御を 先に得ると AppはExtensionが終了するか スコープから退出して 制御をリリースするまで 待ちます ここで 覚えていただきたいのは 排他制御の取得は 失敗する場合があることです 失敗する確率は 非常に低いですが 失敗した場合は コードが処理するべきです 排他制御を取得できないのを 確認するには 関数により生成された エラーが nilでないかを 確認します この後はAppやExtensionが 文脈内で排他的に アクセスできます 先ほどの例に基づくと 必要に応じてファイルの コンテンツを読み 整理することが 有効になりました その他のAppやExtensionが 排他制御に アクセスする機会を 得た場合 すでに ファイルが処理されたのを 知っているからです これを達成するには ファイルが存在するか データベースやPリストに 書かれているか確認します Background Downloader Extensionは Appの大容量のアセットの ダウロードをスケジューリング 収集するためのものです ランタイムが短いので Extension内の作業は 最小限に抑えてください また ExtensionとAppを 共通のAppグループに含み どちらもダウンロードされた ファイルにアクセスできる ようにしましょう 最後にExtensionでは Appではなくシステムが 仲介します 基本的なBackground Download Extensionを 作成する方法を学んだので Background Assetsの 実装の準備が 整いました ではおさらいです Download Managerは AppとExtensionの間の ダウンロードのスケジューリングと 調整を行うために使用します そのため両方で Download Managerを 使用してください Appがフォアグラウンドに いなくてもExtensionは Appインストールや更新時 システムが判断した定期的な 間隔で起動します Appが起動し バックグランドで ダウンロードされていた コンテンツが 応対される場合 即座にダウンロードを フォアグラウンドに送ります Extensionが処理できるのは バックグランドのみです Appがダウンロードを フォアグラウンドに送ることで コンテンツはできる限り 迅速に表示されます Download Managerへの 排他的なアクセスが 必要な場合は排他制御APIを 使用してください そうすればAppやExtensionは その間隔でランタイムを 持ちます これは非常に便利でExtensionが コンテナへのアクセスや ダウンロードの管理で Appと競合するのを 防止します このセッションから 何か参考になることがあるとすれば Appの起動を待つ間の 貧弱な体験です タスクを待つ間に Appを使用可能にして 待ち時間を短縮する方法に 現在取り組んでいます 待ち時間を 短縮する方法の1つとして Background Assets フレームワークと Background Download Extensionを導入しましょう これを使うことにより みなさんのAppは起動する前に コンテンツを 利用可能にできます ドキュメントもご覧ください このセッションでは 説明できなかった Extensionのテスト方法や エントリポイントの シミュレーションなど 他の情報が含まれています みなさんと Background Assetsを 共有し フィードバックを 頂けるのが楽しみです Feedback Assistantを 使用して 長所や改善点を 教えてください これは新しい フレームワークなので 調整する機会は 十分にあります みなさんが興味を持つような 他のセッションもございます 次のセッションを 是非ご覧ください 『HTTP3による ネットワーキングの加速』 Background Assetsと 組み合わせて使えます また別のセッション 『オンデマンド リソースの紹介』では Appleが コンテンツをホストし ファイルはリクエストで ダウンロードできる Background Assetsの 代替品をご紹介します どちらのセッションも 魅力的で とても参考になります ご清聴 ありがとうございました Appleの全社員を代表して すばらしいWWDCを お過ごしください! ♪
-
-
5:28 - Getting started with Background Assets
// Getting started with Background Assets import BackgroundAssets let url = URL(string: "https://cdn.example.com/large-asset.bin")! let appGroupIdentifier = "group.WWDC.AssetContainer" let download = BAURLDownload ( identifier: "Large-Asset", request: URLRequest(url:url), applicationGroupIdentifier: appGroupIdentifier ) let manager = BADownloadManager.shared manager.delegate = self // BADownloadManagerDelegate protocol // Schedule download at an opportunistic time determined by the system do { try manager.schedule(download) } catch { print("Failed to schedule download. \(error)") } // or Schedule download in foreground do { try manager.startForegroundDownload(download) } catch { print("Failed to start foreground download. \(error)") } // or Promote downloads to foreground. do { for download in try await manager.fetchCurrentDownloads) { try manager.startForegroundDownload(download) } } catch { print("Failed to promote downloads to foreground \(error)") }
-
10:28 - BADownloadManager delegate protocol
// BADownloadManager protocol definition public protocol BADownloadManagerDelegate : NSObjectProtocol { optional func downloadDidBegin(_ download: BADownload) optional func downloadDidPause(_ download: BADownload) optional func download(_ download: BADownload, bytesWritten: Int64, totalBytesWritten: Int64, totalExpectedBytes: Int64) optional func download(_ download: BADownload, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) optional func download(_ download: BADownload, failedWithError error: Error) optional func download(_ download: BADownload, finishedWithFileURL fileURL: URL) }
-
15:37 - BADownloaderExtension protocol
// BADownloaderExtension protocol definition public protocol BADownloaderExtension : NSObjectProtocol { optional func applicationDidInstall(metadata: BAApplicationExtensionInfo) optional func applicationDidUpdate(metadata: BAApplicationExtensionInfo) optional func checkForUpdates(metadata: BAApplicationExtensionInfo) optional func download(_ download: BADownload, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) optional func backgroundDownloadDidFail(failedDownload: BADownload) optional func backgroundDownloadDidFinish(finishedDownload: BADownload, fileURL: URL) optional func extensionWillTerminate() }
-
19:40 - Synchronizing between app and extension
// Synchronizing between app and extension func download(_ download: BADownload, finishedWithFileURL fileURL: URL) { let manager = BADownloadManager.shared manager.withExclusiveControl { error in guard error == nil else { print("Unable to acquire exclusive control \(String(describing: error))") return } // Exclusive control acquired // All code in this scope ensures mutual exclusion between extension and app do { let data = try Data(contentsOf: fileURL, options: .mappedIfSafe) // Do something with memory mapped data try FileManager.default.removeItem(at: fileURL) } catch { print("Unable to read/cleanup file data. \(error)") } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。