WWDC Quick Look 💓 By SwiftGGTeam
Meet Background Assets

Meet Background Assets

元の動画を見る

ハイライト

Apple は iOS 16、iPadOS 16、macOS 13 にバックグラウンド アセット フレームワークを導入しました。これにより、開発者はバックグラウンド ダウンロード拡張機能を使用して、アプリケーションのインストール後、最初の起動前に CDN から大きなファイル リソースを自動的にダウンロードできるようになり、アプリケーションを開いた後もリソースのダウンロードを待たなければならないユーザーの問題点が解決されました。

主要内容

待機中のユーザーにとっての問題点

半日かけて App Store でお気に入りのゲームを見つけ、クリックしてダウンロードし、数分またはそれ以上待つと、ようやくインストールが完了します。大きな期待を持ってアプリを開いたところ、大きな進行状況バーが表示されました。アプリはさらに数 GB のリソース パッケージをダウンロードする必要があります。

このエクスペリエンスは、低速ネットワークを使用しているユーザーにとって特に悪影響を及ぼします。多くの人はアプリを直接閉じてアンインストールすることを選択するでしょう。開発者はこれを望んでいませんが、既存のソリューションには制限があります。オンデマンド リソースはリソースを Apple にホストする必要があります。 URLSession バックグラウンド ダウンロードは、アプリケーションの起動後にのみ使用できます。

バックグラウンド アセットのソリューション アイデア

Apple のソリューションは、まったく新しいフレームワークであるバックグラウンド アセットです。新しいアプリケーション拡張機能であるバックグラウンド ダウンロード拡張機能が導入され、アプリケーションのライフサイクルの外で実行されます。

拡張機能はシステムによって次の 3 つの時点で起動されます。

  1. 最初のインストール後: ユーザーがアプリを開く前に、拡張機能はリソースのダウンロードを開始します。
  2. アプリケーションの更新後: App Store がアプリケーションを自動的に更新した後、拡張機能が起動して新しいリソースをダウンロードします。
  3. 定期的な操作: ユーザーの使用頻度に基づいて、システムは定期的に拡張機能を起動して更新を確認します。

開発者は拡張機能でダウンロード タスクをスケジュールするだけで、システムがそれを実行する適切な時間を選択します。ダウンロードが完了すると、ファイルはアプリ グループの共有コンテナに保存され、アプリケーションの起動時に直接使用できるようになります。

既存のソリューションとの関係

バックグラウンド アセットは柔軟に設計されています。すでに複雑なリソース管理システムがある場合は、それをシームレスに統合できます。これは、Apple がホストするオンデマンド リソースを補完する、独自の CDN からリソースをダウンロードする必要があるシナリオに適しています。

詳細

BADownloadManager: コア スケジューリング インターフェイス

05:28

BADownloadManagerこれはフレームワークのコア クラスであるシングルトン オブジェクトであり、アプリケーションと拡張機能を通じて実行されます。

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

// 由系统决定最佳时机执行
do {
    try manager.schedule(download)
} catch {
    print("Failed to schedule download. \(error)")
}

// 或立即在前台执行
do {
    try manager.startForegroundDownload(download)
} catch {
    print("Failed to start foreground download. \(error)")
}

キーポイント:

  • BAURLDownloadidentifier一意である必要があります。システムでは、同じ識別子を使用してダウンロードを繰り返しスケジュールすることは許可されていません
  • applicationGroupIdentifierアプリグループを指定します。ダウンロードしたファイルを共有するには、アプリと拡張機能が同じグループに参加する必要があります。
  • schedule(_:)拡張での使用に適した実行に最適な時間をシステムに選択させます
  • startForegroundDownload(_:)すぐに開始され、優先度が高く、アプリケーションのメイン プログラムからのみ呼び出すことができます。

バックグラウンドでのダウンロードをフォアグラウンドに促進する

07:58

拡張機能によってスケジュールされたバックグラウンド ダウンロードがまだ進行中にユーザーがアプリを開いた場合は、すぐにダウンロードをフォアグラウンドに移動する必要があります。

do {
    for download in try await manager.fetchCurrentDownloads() {
        try manager.startForegroundDownload(download)
    }
} catch {
    print("Failed to promote downloads to foreground \(error)")
}

キーポイント:

  • fetchCurrentDownloads()スケジュールされたダウンロード、進行中のダウンロード、またはキューに入れられたダウンロードをすべて返します。
  • フォアグラウンドに昇格した後、ダウンロードされた部分は再ダウンロードされず、ブレークポイントから継続されます。
  • 拡張機能自体には UI がなく、ユーザーが認識できないため、フォアグラウンド ダウンロード API を呼び出すことができません。

委任契約: ダウンロード ステータスの監視

10:28

BADownloadManagerDelegateプロトコルはダウンロード ステータス コールバックを受け取ります。

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)
}

キーポイント:

  • すべてのデリゲート メソッドはオプションであり、オンデマンドで実装されます
  • downloadDidPauseバックグラウンドでのダウンロードがフォアグラウンドに昇格するとトリガーされ、途中で短い一時停止が発生します。
  • 正常にダウンロードされたファイルはシステムによって管理され、ストレージ容量が不足するとシステムが自動的にファイルをクリーンアップします。
  • ファイルはシステムが提供する場所に保存することをお勧めします。移動する必要がある場合は、繰り返しスペースが占有されるのを避けるために、移動後に元のファイルを削除してください。

バックグラウンド ダウンロード拡張機能: アプリケーションが起動していないときに実行されます。

15:37

拡張機能を実装する必要があるBADownloaderExtensionプロトコル:

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()
}

キーポイント:

  • applicationDidInstall: アプリケーションの初回インストール後に呼び出され、初期リソース パッケージのダウンロードに適しています。
  • applicationDidUpdate: App Store がアプリを更新した後に呼び出されます
  • checkForUpdates: システムは定期的に起動します。その頻度はユーザーの使用頻度によって異なります。
  • 拡張のライフサイクルは非常に短いです。ダウンロードを迅速にスケジュールすることのみが可能であり、解凍などの時間のかかる操作は実行できません。
  • 拡張機能の実行頻度はアプリケーションの使用状況に基づいて動的に調整されます。一般的に使用されるアプリケーション拡張機能はより頻繁に実行されます。

Info.plist 構成要件

12:56

アプリケーションのメイン Info.plist には、次の 2 つのキーを追加する必要があります。

  1. BAInitialDownloadRestrictions(辞書):
    • DownloadAllowance: 最初のインストール時にダウンロードできる最大合計バイト数 (すべてのファイルの合計)
    • AllowList: ダウンロードが許可されるドメイン名の配列。プレフィックス ワイルドカードをサポートします。
  • これらの制限は最初のインストール時にのみ有効で、アプリの起動後は制限されなくなります。
  1. BADownloadSize(ルートレベル):
  • 解凍後のリソースの合計サイズ
  • この値は、追加コンテンツをどれだけダウンロードする必要があるかをユーザーに知らせるために App Store に表示されます。

アプリケーションと拡張機能間の同期メカニズム

19:40

拡張機能とアプリケーションが同時に実行され、競合状態が発生する可能性があります。たとえば、拡張機能がディレクトリ ファイルをダウンロードする場合、アプリケーションと拡張機能の両方がそれを読み取り、削除する必要があります。

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
        }
        // 此范围内的代码与扩展互斥
        do {
            let data = try Data(contentsOf: fileURL, options: .mappedIfSafe)
            // 处理数据
            try FileManager.default.removeItem(at: fileURL)
        } catch {
            print("Unable to read/cleanup file data. \(error)")
        }
    }
}

キーポイント:

  • withExclusiveControlアプリと拡張機能が重要なコードを同時に実行しないようにする
  • 一方が排他制御を取得した場合、もう一方は待機します。
  • 排他制御の取得に失敗する可能性があり、エラー状況に対処する必要があります。
  • 拡張機能が終了すると自動的に排他制御が解除されます

重要ポイント

  • ゲーム リソースのプリロード: アプリケーションのインストール後に、高解像度のテクスチャ パックとレベル データを自動的にダウンロードします。ユーザーは、初めてゲームを開いたときに、待たずに直接メイン インターフェイスに入ります。入口API:applicationDidInstallスケジュール半ばBAURLDownload

  • オフライン コンテンツ キャッシュ: ニュース アプリや読書アプリは、最新の記事、写真、ビデオをバックグラウンドで定期的にダウンロードします。ユーザーがアプリを開いた時点でコンテンツは準備完了です。入口API:checkForUpdatesサーバーのバージョン番号を確認し、増分ダウンロードをスケジュールします。

  • AI モデルの動的更新: 機械学習アプリケーションは、更新された Core ML モデル ファイルをバックグラウンドでダウンロードします。ユーザーは常に最新モデルを使用できるため、アプリを手動で更新する必要がなくなります。入口 API: 拡張機能でダウンロード.mlmodelcファイル、ロードされるMLModel

  • マップ データ パッケージ管理: ナビゲーション アプリケーションは、都市または地域ごとにオフライン マップ パッケージをダウンロードします。初回インストール時には、ユーザーがいる都市の基本パッケージがダウンロードされ、その後、旅程に応じて経由する都市の地図が自動的にダウンロードされます。入口API:applicationDidInstall + checkForUpdates組み合わせて使用​​してください。

  • ビデオ コースの事前キャッシュ: 教育アプリケーションは、ユーザーが登録した次のクラスのビデオ ファイルを夜間に自動的にダウンロードします。ユーザーは翌日アプリを開いたときにオフラインで視聴できます。入口 API: ユーザーの学習進行状況データと組み合わせて、拡張機能でダウンロードする必要があるコース ID を決定します。

関連セッション

  • HTTP/3 によるネットワークの高速化 - ネットワーク伝送効率を向上させ、バックグラウンド アセットと連携して大きなファイルのダウンロードを最適化します。
  • SwiftUI の新機能 — SwiftUI を使用して、プリロードされたリソースを必要とするアプリケーション インターフェイスを構築する
  • SwiftData の紹介 — アプリケーション データ モデルを管理し、リソースのダウンロードを操作する

コメント

GitHub Issues · utterances