2024年春以降、Privacy Manifests未対応のiOSアプリはリジェクトされてしまう
AppleはWWDC23でプライバシーの扱いについての大きな変更を発表した。2024年春以降、Privacy Manifestsに対応していないアプリは新規申請・アップデート審査時にリジェクトされてしまうようだ。
「2024年春」が具体的にいつかは不明だが、2024年3月と仮定すると、期限はすでに4ヶ月を切っている。土壇場で慌てないために、本記事では私が個人開発しているアプリを使って、Privacy Manifests対応のプロセスを紹介する。
後述するが、Privacy Manifests対応とは「アプリ」と「SDK(ライブラリ)」の両方の対応が必要になる。本記事では「アプリ」での対応をメインに書いている。「ライブラリ」のプライバシーマニフェスト対応状況については、別途「サードパーティSDKのPrivacy Manifests対応状況を調査した」にて書いているので、こちらも参考にしていただきたい。
Privacy Manifestsの概要
Privacy Manifestsに関する詳細は、リルオッサ氏のレポート「【iOSDC Japan 2023 レポート】「基礎から理解する!来年春までに対応すべきプライバシーの変更点」を聞いてきた。」を参照すること。
今回追加されたプライバシー要件は、アプリに組み込まれているサードパーティSDKでのプライバシー情報の取り扱いがブラックボックスであったため、さらにはアプリのプライバシー情報の表記の自動化を視野に入れているため、追加された要件だと個人的には理解している。Appleは、2023年6月に「年内に情報を公開する」と告知し、12月7日にようやく追加情報が公開された。
対応するアプリの現状分析
私が開発しているiOSアプリ「Four Cropper」は、ユーザーが画像を選択し、四分割するシンプルなアプリだ。このアプリでは、「設定値の永続化」や「サブスク対応」などを取り入れている。私は機能提案のための調査でよく実験台として使っている。今回、プライバシー対応で関連があるのは以下のポイントである。
- UserDefaultsを使った設定の永続化
- 導入済みのサードパーティ製ライブラリ
- Firebase AnalyticsやCrashlyticsによるアプリ統計情報の取得
- RevenueCatを通じたサブスクリプション対応
Privacy Manifests対応でやること
Four CropperのPrivacy Manifests対応でやることは大きく分けて3つである。
- UserDefaultsなど、アプリで利用しているAPIの宣言
- 「アプリのプライバシー」の宣言
- サードパーティSDKでの PrivacyInfo.xcprivacy 対応
使用しているAPIの宣言
開発者は自身のアプリが使用しているAPIがPrivacy Manifestsの要件に該当するかどうかを確認し、使用しているAPIグループと理由を宣言する必要がある。
該当するAPIについての詳細は「Apple Developer Documentation」にて確認することができる。
APIグループ | アクセス対象 | Key名 |
---|---|---|
File timestamp APIs | ファイルのタイムスタンプ | NSPrivacyAccessedAPICategoryFileTimestamp |
System boot time APIs | システム起動時間 | NSPrivacyAccessedAPICategorySystemBootTime |
Disk space APIs | 使用可能なディスク容量 | NSPrivacyAccessedAPICategoryDiskSpace |
Active keyboard APIs | アクティブなキーボードのリスト | NSPrivacyAccessedAPICategoryActiveKeyboards |
User defaults APIs | UserDefaults | NSPrivacyAccessedAPICategoryUserDefaults |
前述の通り、Four Cropper ではUserDefaultsを使って設定値を保存している。アプリ自身しかアクセスできない情報の読み書きに使っているので理由として「CA92.1」を指定すればよい。
「アプリのプライバシー」の宣言
「アプリのプライバシー」をPrivacyInfo.xcprivacyで定義する。これは、App Storeのアプリページに表示される情報と等価である。「アプリのプライバシー」の入力が求められるようになって数年経つため、大抵のアプリではすでに対応済みだろう。ここでの作業は、自分のアプリのWebページを確認しながら plist形式で転記していくだけだ。
余談ではあるが、「アプリのプライバシー」をどう設定すればよいかについては、サードパーティSDKの提供元でドキュメントが公開されているケースが多い。たとえば、AdMob や RevenueCatを使っている場合は下記に従って入力していけばよいだろう。
サードパーティSDKの対応
サードパーティSDKのPrivacyInfo.xcprivacy対応も必要である。Apple は、「プライバシーマニフェストと署名の対象となるSDK」のリストを一部公開している。
現状、我々アプリ開発者にできることはない。各サードパーティでの対応を待つしかないので、時々公式サイトでリリースノートを確認して欲しい。アプリ開発でよく使われている Firebase は GitHub 上で対応について議論しているようだ。
Four Cropperでは、Firebase AnalyticsやCrashlytics、RevenueCatを使用している。少なくともこれらのサードパーティSDKのPrivacy Manifests対応が完了しない限り、アプリとしても対応完了とは言えないだろう。
他にもよく使われている 画像キャッシュライブラリのKingfisherやSDWebImage、プログレスダイアログを表示するMBProgressHUDやSVProgressHUD、通信ライブラリのAlamofireなどが対象になっており、少なくともこれらのSDKのPrivacy Manifests対応を待つ必要がある。
2024年1月11日追記: サードパーティSDK(ライブラリ)ごとのPrivacy Manifests対応状況がリストで確認したかったので、「サードパーティSDKのPrivacy Manifests対応状況を調査した」にまとめた。
PrivacyInfo.xcprivacy の具体的な実装方法
PrivacyInfo.xcprivacyファイルをプロジェクトに追加して、アプリが使用するデータの種類と目的を宣言する。
さて、実際に既存のアプリにプライバシー情報を追加していく。「New Files...」を選択し、ファイルを追加する。リソースカテゴリの App Privacy を選択し、Nextボタンをクリックする。
ファイル名はデフォルトの「PrivacyInfo」のままとする。ソースコードの追加時と異なり、デフォルトでターゲットが選択されていないため、どのターゲットに PrivacyInfo.xcprivacy を追加するか選択する必要がある。このタイミングで忘れずに対象のターゲットにチェックをつけておくと良い。
このアプリでは UserDefaults を利用しているため、以下のように入力した。
次に「アプリのプライバシー」の宣言では、以下のようにデータカテゴリを追加する。Four Cropper では AdMob を利用して広告を表示させているため、「位置情報(おおよその場所)」を「ユーザーのトラッキング」に利用しており「ユーザーに関連付け」している。そのため、以下のように入力した。
同様にApp Storeのアプリページの「アプリのプライバシー (詳細)」を確認しながら、残りのデータカテゴリについても追加していく。最終的に PrivacyInfo.xcprivacy は以下のようになった。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSPrivacyCollectedDataTypes</key> <array> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypeCoarseLocation</string> <key>NSPrivacyCollectedDataTypeLinked</key> <true/> <key>NSPrivacyCollectedDataTypeTracking</key> <true/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> </array> </dict> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypeDeviceID</string> <key>NSPrivacyCollectedDataTypeLinked</key> <true/> <key>NSPrivacyCollectedDataTypeTracking</key> <true/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> </array> </dict> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypePurchaseHistory</string> <key>NSPrivacyCollectedDataTypeLinked</key> <true/> <key>NSPrivacyCollectedDataTypeTracking</key> <false/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> <string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string> </array> </dict> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypeProductInteraction</string> <key>NSPrivacyCollectedDataTypeLinked</key> <true/> <key>NSPrivacyCollectedDataTypeTracking</key> <true/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> <string>NSPrivacyCollectedDataTypePurposeAnalytics</string> </array> </dict> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypeAdvertisingData</string> <key>NSPrivacyCollectedDataTypeLinked</key> <true/> <key>NSPrivacyCollectedDataTypeTracking</key> <true/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> <string>NSPrivacyCollectedDataTypePurposeAnalytics</string> </array> </dict> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypeCrashData</string> <key>NSPrivacyCollectedDataTypeLinked</key> <false/> <key>NSPrivacyCollectedDataTypeTracking</key> <true/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> <string>NSPrivacyCollectedDataTypePurposeAnalytics</string> </array> </dict> <dict> <key>NSPrivacyCollectedDataType</key> <string>NSPrivacyCollectedDataTypePerformanceData</string> <key>NSPrivacyCollectedDataTypeLinked</key> <true/> <key>NSPrivacyCollectedDataTypeTracking</key> <true/> <key>NSPrivacyCollectedDataTypePurposes</key> <array> <string>NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising</string> <string>NSPrivacyCollectedDataTypePurposeAnalytics</string> </array> </dict> </array> <key>NSPrivacyAccessedAPITypes</key> <array> <dict> <key>NSPrivacyAccessedAPITypeReasons</key> <array> <string>CA92.1</string> </array> <key>NSPrivacyAccessedAPIType</key> <string>NSPrivacyAccessedAPICategoryUserDefaults</string> </dict> </array> </dict> </plist>
以上でアプリにプリバシー情報を追加する作業は完了だ。続いて追加した宣言が正しく設定されているかどうかを確認していく。
プライバシーレポートを生成して正しく実装できているか検証する
Xcode 15.1時点では、PrivacyInfo.xcprivacyを自動生成する方法はない。正しく設定できているかを確認するためには「プライバシーレポートを生成して確認する」が唯一の方法である。プライバシーレポートの生成プロセスは以下の通りである。
- アーカイブを作成する: Xcodeで「Product > Archive」を選択する。アプリのアーカイブを作成する
- プライバシーレポートを生成する: オーガナイザーで、アーカイブした項目を右クリックして「Generate Privacy Report」を選択する
- プライバシーレポートを保存する: プライバシーレポートを任意のパスに出力する
- プライバシーレポートを確認する: プライバシーレポート(PDFファイル)をダブルクリックして開き、内容を確認する
アーカイブした項目を右クリックし、コンテキストメニューから「Generate Privacy Report」を選択する。
任意のパスにプライバシーレポートを出力されるので、ダブルクリックしてレポートを確認する。
出力したプライバシーレポートが、App Store のアプリページの下部にある「アプリのプライバシー(詳細)」の各項目と対応しているかを確認する。
トラブルシューティング:PrivacyInfo.xcprivacyの記載にミスがあると空のPDFが生成される
PrivacyInfo.xcprivacy
の記載にミスがあっても基本的にはエラーは発生しない。単に空のPDFが生成されるだけである。空のPDFが生成された場合、PrivacyInfo.xcprivacy
の設定にどこか誤りがあることを示している。
しかし、どの設定が悪いかは教えてくれないので、ハマってしまった場合は手間がかかってしまうが、新規にPrivacyInfoを追加して、ゼロから少しずつ設定を追加し、レポートを出力して確認する、という方法で作業を進めた方が良いだろう。
まとめ
Privacy Manifestsへの対応は、期限が決まっており、あらかじめ段取りをしておく必要がある。アプリ開発者としてユーザーに対する説明責任を果たし、ユーザーの信頼を得るために不可欠であるため、新しいプライバシー要件に迅速に対応することが求められる。一方でサードパーティSDKの対応を待つ必要があり、そちらがボトルネックとなり非常にもどかしい部分もある。
また、現時点ではPrivacy ManifestsとApp Store Connectでプライバシー情報を二重管理しないといけないのも面倒な点だ。過去にアプリアイコンを App Store Connect にわざわざアップロードしていたものが、アプリバイナリから読み込まれるようになったように、そのうち手入力でプライバシー情報を入力しなくてもよくなる日が来ることを期待している。
付録:データのカテゴリと収集理由
「Describing data use in privacy manifests | Apple Developer Documentation」より。アプリまたはサードパーティSDKが収集するデータの「カテゴリ」と「収集理由」は以下の通り。
値こそはいままで表に出てきていなかったが、データタイプについては App Store Connect の「アプリのプライバシー」にて既出のものである。
データカテゴリ
分類 | データタイプ | 値 |
---|---|---|
連絡先情報 | 名前 | NSPrivacyCollectedDataTypeName |
メールアドレス | NSPrivacyCollectedDataTypeEmailAddress | |
電話番号 | NSPrivacyCollectedDataTypePhoneNumber | |
所在地 | NSPrivacyCollectedDataTypePhysicalAddress | |
ユーザのその他の連絡先情報 | NSPrivacyCollectedDataTypeOtherUserContactInfo | |
健康とフィットネス | 健康 | NSPrivacyCollectedDataTypeHealth |
フィットネス | NSPrivacyCollectedDataTypeFitness | |
財務情報 | 支払い情報 | NSPrivacyCollectedDataTypePaymentInfo |
クレジット情報 | NSPrivacyCollectedDataTypeCreditInfo | |
その他の財務情報 | NSPrivacyCollectedDataTypeOtherFinancialInfo | |
位置情報 | 詳細な位置情報 | NSPrivacyCollectedDataTypePreciseLocation |
おおよその場所 | NSPrivacyCollectedDataTypeCoarseLocation | |
機密情報 | 機密情報 | NSPrivacyCollectedDataTypeSensitiveInfo |
連絡先 | 連絡先 | NSPrivacyCollectedDataTypeContacts |
ユーザコンテンツ | メールまたはテキストメッセージ | NSPrivacyCollectedDataTypeEmailsOrTextMessages |
写真またはビデオ | NSPrivacyCollectedDataTypePhotosorVideos | |
オーディオデータ | NSPrivacyCollectedDataTypeAudioData | |
ゲームプレイコンテンツ | NSPrivacyCollectedDataTypeGameplayContent | |
カスタマーサポート | NSPrivacyCollectedDataTypeCustomerSupport | |
その他のユーザコンテンツ | NSPrivacyCollectedDataTypeOtherUserContent | |
閲覧履歴 | 閲覧履歴 | NSPrivacyCollectedDataTypeBrowsingHistory |
検索履歴 | 検索履歴 | NSPrivacyCollectedDataTypeSearchHistory |
ID | ユーザーID | NSPrivacyCollectedDataTypeUserID |
デバイスID | NSPrivacyCollectedDataTypeDeviceID | |
購入 | 購入 | NSPrivacyCollectedDataTypePurchaseHistory |
使用状況データ | 製品の操作 | NSPrivacyCollectedDataTypeProductInteraction |
広告データ | NSPrivacyCollectedDataTypeAdvertisingData | |
その他の使用状況データ | NSPrivacyCollectedDataTypeOtherUsageData | |
診断 | クラッシュデータ | NSPrivacyCollectedDataTypeCrashData |
パフォーマンスデータ | NSPrivacyCollectedDataTypePerformanceData | |
その他の診断データ | NSPrivacyCollectedDataTypeOtherDiagnosticData | |
その他のデータ | その他のデータ | NSPrivacyCollectedDataTypeOtherDataTypes |
収集理由
理由 | 値 |
---|---|
サードパーティ広告 | NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising |
デベロッパの広告またはマーケティング | NSPrivacyCollectedDataTypePurposeDeveloperAdvertising |
アナリティクス | NSPrivacyCollectedDataTypePurposeAnalytics |
製品のパーソナライズ | NSPrivacyCollectedDataTypePurposeProductPersonalization |
アプリの機能 | NSPrivacyCollectedDataTypePurposeAppFunctionality |
その他の目的 | NSPrivacyCollectedDataTypePurposeOther |
(2024/3/22追記) 新しいプライバシー要件が有効になった
2月末にApp Storeへの提出における新しいプライバシー要件についての具体的な施行スケジュールが発表された。
3月13日からレビュー提出時にプライバシーマニフェストのチェックされ、不備があれば警告メールが発信される。5月1日からはプライバシーマニフェストの遵守が求められるようになり、不備がある場合にリジェクトされる。
3月14日時点で、プライバシーマニフェスト対応が不完全なアプリを、レビューに提出した際に送られてくる警告メールについて、以下の記事にまとめた。