ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
PhotoKitの変更履歴について
PhotoKitにより、写真を中心とした豊富な機能を構築できるようになります。PhotoKitの最新APIを使用して、画像アセットへの変更を簡単に追跡する方法について解説します。さらに、PHPhotoLibraryのchange historyAPIを紹介し、起動時に変更トークンを保持する方法、Appで他のユーザの写真ライブラリへの追加、削除、更新を認識できるようにする方法についても紹介します。 写真ライブラリの統合に関する詳細については、WWDC22の「写真ピッカーの最新情報」と、WWDC21の「App内の写真アクセスの改善」をご覧ください。
リソース
関連ビデオ
WWDC23
WWDC22
WWDC21
-
ダウンロード
♪ 落ち着いた ヒップホップの曲 ♪ ♪ Photosチームの エンジニアのMindyです 今日は Appで 写真の変更履歴に アクセスする方法を 説明します PhotoKitの 豊富なAPIセットでは 写真ライブラリの写真 ビデオ アルバムへの アクセスと 更新が可能です PhotoKitは深いレベルで 写真へのアクセスと 統合が必要なApp向けに 設計されています 写真の管理と編集のための Appや カスタムカメラApp 写真のライブラリを 独自の方法で 閲覧できるAppなどです このようなタイプの Appでは 写真ライブラリの 変化を監視し 写真のエクスペリエンスを 忠実に反映したいものです ハイキング用SNSを 作ったとしましょう 友達と ハイキングの写真を 共有・編集できるAppです Appを起動すると 最近のハイキングの開始と 終了のタイムスタンプから 写真が集められ 山の体験の コラージュが生成されます 写真ライブラリから 選択された写真と コラージュは同期しています ハイキングの写真を 友人から受け取ると その更新を使用して 新しいコラージュを作ります これまでは Appが 新しいアセットの挿入や 既存のコラージュへの 変更を検出するには 一連の取得の実行が 必要でした 挿入されたアセットを 判別するには 作成日が 最後のApp起動日より 新しいアセットを取得します アセットの更新や削除の 判別は より困難です 全コラージュ内の 全アセットを再取得し 変更日をチェックして アセットの更新を判別します ただし アセットの変更日は 写真の内部の処理作業で 設定されうるため 誤検知の可能性もあります 写真ライブラリ内の削除は 追跡がより困難です 追跡対象の全アセットを 取得し 返されなかった アセットの差分を取ります つまり 3つの別々のチェックが Appを起動するたびに 必要になります 大量のアセットを 表示するAppでは 特にコストがかかります 様々な取得とチェックを 実行して 不確かな結果を得る代わりに 1回の統一された APIの呼び出しで 変更点を 正確に知る方法があったら? 私たちは まさに それを実現しました 新しいchange history APIを 使えば 簡単に 写真ライブラリの オフライン更新を追跡できます 変更履歴は 写真ライブラリへの 挿入 更新 削除などの 変更の タイムラインで 構成されています このタイムラインの例では 過去3日間の変更履歴に アセット アルバム フォルダの様々な変更が あります このタイムラインを使い 過去2日間の または Appの最終起動時の 変更を判別するには どうしたらよいでしょう? 永続的変更トークンが 使用可能になりました ある時点の 写真ライブラリの 状態を示すものです このトークンは App終了後も保持され そのトークン以降の 写真ライブラリの変更を 取得するのに使用できます サードパーティAppの 変更も含みます Appのライブラリへのアクセスが 制限されている場合は ユーザーが選択した PhotoKitオブジェクトの 変更のみが返されます この変更トークンは デバイスローカルで 永続的変更や 写真ライブラリの インスタンスから いつでも 簡単にアクセスできます この新APIの利用は PhotoKit対応の macOS・iOS・iPadOS・tvOS 全プラットフォームで可能です Appの実行時 写真ライブラリの操作中に App内に永続的変更 トークンを保存できます 後で そのトークンを使用し 写真ライブラリの 以降の変更を取得できます 永続的変更ごとに 写真オブジェクトの3種類の 詳細を取得できます アセット・アセットコレクション コレクションリストです これは コードでは どうなるでしょう? まず 最後に保存された トークンを使用し 永続的変更を取得します 次に 永続的変更を列挙し 変更の詳細 ここでは “アセット”タイプを 永続的変更オブジェクト ごとに取得します これらの変更の詳細には トークン以降に更新 削除 写真ライブラリへの挿入が 行われたローカル識別子の 情報が含まれています これらの変更の処理後 今後使うために 最後の 変更トークンを保存できます 新しい永続的な change history APIと 従来のchange observer APIを比較しましょう PHChangeはアクティブな メモリ内の取得結果を処理し Appの実行中に 写真ライブラリへの ライブ変更を記録します 一方 永続的な change history APIは 写真ライブラリへの変更を 長期間記録し Appの 非アクティブ時の 変更履歴を報告できます 開発するAppの要件に応じて これらのAPIの両方または どちらかを使用できます ハイキングAppの例に 戻ります 永続的なchange history APIを使って アセットの変更を追跡し ハイキングの コラージュを 作成・更新します まず 最後に保存された 変更トークンを使用し 永続的変更を取得します 次に 永続的変更を 繰り返し処理し 関連アセットの 変更の詳細を取得し 挿入 更新 削除が行われた 識別子を処理します ここで Appに影響する 写真ライブラリの変更を 変更履歴から 特定する必要があります 取得された変更の 全情報までは このAppでは 必要としないためです このAppで重要なのは 最近のハイキングで 追加されたアセットと 以前のコラージュで 参照された後 更新 削除されたアセットを 知ることです 挿入・更新・削除された アセットのローカル識別子の 3セットは 永続的変更の列挙で 特定しましたが これを反映するように Appを更新するには? InsertedIdentifiersセットを 使用すると ハイキングの タイムスタンプの間に 挿入したアセットを 特定できます 挿入したアセットの作成日を 各ハイキングの開始・ 終了日と照合するのです 更新したアセットは 調整された可能性があるので 新しいhasAdjustments APIを使用して UIでアセットの再描画が 必要かどうかを確認します 削除されたアセットは ローカル識別子を使用して 再生成が必要な コラージュを決定できます これで オフラインの 写真ライブラリの変更を すべて処理し Appは 最新の状態になりました 新しいchange history APIを 使用する際に 留意すべき点が いくつかあります まず あなたと あなたのAppにとって 重要な変更のみを 確認すること パフォーマンス向上のために 複数回の小さな リクエストではなく 大きな取得リクエスト1回の 実行を検討すること 写真ライブラリは 内部処理と同期により 大幅に変更される 可能性があるため 特に Appが頻繁に 起動されない場合は 大量の変更を列挙する 可能性があります UIをブロックしないよう バックグラウンドスレッドで 変更履歴を 要求することをお勧めします 永続的変更履歴を 取得する際に 発生する可能性のある エラーは2種類 変更トークンが 利用可能な変更履歴よりも 古い場合 期限切れの変更トークン エラーが返されます 永続的変更に基づいて これまでの変更の 完全な再構築が 行えない場合には 変更の詳細が利用できない というエラーが返されます このような場合 写真ライブラリの 追跡オブジェクトを 再取得し Appが 最新か確認してください 最後に PhotoKitの 新しいAPIを さらにいくつか紹介します Media subtypeと Smart albumにより シネマティックモードの ビデオにアクセス可能に 新しいエラーコードも 2つ追加されました 写真ライブラリが macOSの File Provider同期ルート ディレクトリにある場合 ライブラリが破損する 恐れがあり 変更を実行しようとすると エラーが返されます ネットワークの問題で アセットリソースが 見つからない場合は リソースリクエストに ネットワークエラーが 返されます 最新の アップデートについては デベロッパ向けドキュメントを ご確認ください 最後に ぜひ今年の 写真ピッカーの セッションを ご覧ください 写真の操作とアクセスが 一番簡単になる方法です 新しいchange history APIと PhotoKitの素晴らしい新機能を ご利用いただくのが 楽しみです ありがとうございました ♪
-
-
0:01 - Tracking photo library changes
// Discover added assets let options = PHFetchOptions() options.predicate = NSPredicate(format: "creationDate > %@", lastLaunchDate as CVarArg) let insertedAssets = PHAsset.fetchAssets(with: options)
-
0:02 - Tracking photo library changes (2)
let fetchResult = PHAsset.fetchAssets(with: localIdentifiers, options: nil) // Discover all modified and deleted assets fetchResult.enumerateObjects { asset, idx, stop in if asset.modificationDate?.compare(lastLaunchDate) == .orderedDescending { // Asset could have been modified } if !localIdentifiers.contains(asset.localIdentifier) { // Asset could have been deleted } }
-
0:03 - Fetching persistent changes using persistent change token
let persistentChanges = try! PHPhotoLibrary.shared().fetchPersistentChanges(since: self.lastStoredToken) for persistentChange in persistentChanges { if let changeDetails = persistentChange.changeDetails(for: PHObjectType.asset) { let updatedIdentifiers = changeDetails.updatedLocalIdentifiers let deletedIdentifiers = changeDetails.deletedLocalIdentifiers let insertedIdentifiers = changeDetails.insertedLocalIdentifiers } } // After processing change details self.lastStoredToken = lastPersistentChange.changeToken
-
0:04 - Identifying important changes
// Get last stored change token let changeToken = self.lastStoredToken // Fetch persistent changes let persistentChanges = try! library.fetchPersistentChanges(since: changeToken) for persistentChange in persistentChanges { // Grab change details and process updates }
-
0:05 - Using inserted identifiers
let insertedAssets = PHAsset.fetchAssets(with: insertedIdentifiers, options: nil) insertedAssets.enumerateObjects { asset, idx, stop in for hike in hikes { let dateInterval = NSDateInterval(start: hike.startDate, end: hike.endDate) if dateInterval.contains(asset.creationDate) { // This hike contains a new added asset } } }
-
0:06 - Using updated identifiers
let updatedAssets = PHAsset.fetchAssets(with: updatedIdentifiers, options: nil) updatedAssets.enumerateObjects { asset, idx, stop in if asset.hasAdjustments { // This asset has edits } }
-
0:07 - Using deleted identifiers
for deletedIdentifier in deletedIdentifiers { for collage in collages { if collage.assetLocalIdentifiers.contains(deletedIdentifier) { // This collage needs to be redrawn } } }
-
0:08 - Handling errors
do { let persistentChanges = try library.fetchPersistentChanges(since: changeToken) } catch PHPhotosError.persistentChangeTokenExpired, PHPhotosError.persistentChangeDetailsUnavailable { let fetchResult = PHAsset.fetchAssets(with: trackedIdentifiers, options: options) // Use fetch result }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。