【Apple Privacy Manifests対応】【完全版】やることは4つだけ

# はじめに
<!-- 発端や概要を記載 -->
「Privacy Manifestsに対応してね。[2024年5月1日から審査通さないよ！](https://developer.apple.com/jp/news/?id=3d8a9yyh)」
というお達しが出ています。

正直、Privacy Manifestsの調査と対応を終えた自分の心には、
「この対応のためにAppleはどれだけの人間の時間を奪っているんだ...」
という感情だけが残りました。

世の中の開発者にはこんなところじゃなくもっと別のところで時間を使って欲しい。
また、少しでも人の助けになればと思い、本記事を書いてみることにしました。

:::note info

読みやすいように、できるだけ簡単な日本語で書きます。

:::


* （2024/04/01）実際にやってみて、躓いたことを投稿しました

https://qiita.com/propagandazm/items/e96031daa011e4cccd19

# 目次
<!-- タイトルとアンカー名を編集 -->
- [結論](#Conclusion)
- [確認方法（1. 特定のSDKを使っているか）](#Chapter1)
- [確認方法（2. 特定のAPIを使っているか）](#Chapter2)
- [確認方法（3. 特定のPrivacyデータを使っているか）](#Chapter3)
- [確認方法（4. App Tracking Transparencyフレームワークを使っているか）](#Chapter4)
- [終わりに](#Finally)
- [参考](#References)

<!-- 各チャプター -->
<a id="#Conclusion"></a>
# 結論

結論として、Privacy Manifestsに対応するためには、
自分のアプリについて**4つの確認**が必要です。

1. 特定のSDKを使っているか
2. 特定のAPIを使っているか
3. 特定のPrivacyデータを使っているか
4. App Tracking Transparencyフレームワークを使っているか

逆にこの確認で全量になるため、これ以上の確認・対応は不要です。
後述の各章で、考え方や確認手順を記載します。

<a id="#Chapter1"></a>
# 確認方法（1. 特定のSDKを使っているか）

情報の出所はここです。

https://developer.apple.com/jp/support/third-party-SDK-requirements/

Appleの指名したSDKたちが記載されています。
指名されたSDKの**開発者サイド**は、
プライバシーマニフェストの対応とコード署名するように指示されています。

自分たちのようなSDKを使う側の対応としては、
その対応が完了した版のSDKをアプリに組み込む必要があります。

:::note info

各SDKの対応状況については、以下のようなサイトだったり、
SDKのリポジトリを直接見に行ったりしてください。
[サードパーティSDKのPrivacy Manifests対応状況を調査した | DevelopersIO](https://dev.classmethod.jp/articles/surveyed-the-status-of-privacy-manifests-support-for-third-party-sdks/)

:::

SDK側で対応していれば、そのバージョンに上げましょう。
対応していない・する予定がないSDKについては、
残念ながら別SDKの利用を検討する必要があります。。

「そもそもこれらのSDK使ってるんだっけ？」の確認については、
シェルコマンドを作ったのでご活用ください。
（手作業でgrepするよりは格段に効率が良いと思います。）

```shell
echo "find . -type f | xargs grep Abseil:"
find . -type f | xargs grep Abseil
echo "find . -type f | xargs grep AFNetworking:"
find . -type f | xargs grep AFNetworking
echo "find . -type f | xargs grep Alamofire:"
find . -type f | xargs grep Alamofire
echo "find . -type f | xargs grep AppAuth:"
find . -type f | xargs grep AppAuth
echo "find . -type f | xargs grep BoringSSL／openssl_grpc:"
find . -type f | xargs grep 'BoringSSL／openssl_grpc'
echo "find . -type f | xargs grep Capacitor:"
find . -type f | xargs grep Capacitor
echo "find . -type f | xargs grep Charts:"
find . -type f | xargs grep Charts
echo "find . -type f | xargs grep connectivity_plus:"
find . -type f | xargs grep connectivity_plus
echo "find . -type f | xargs grep Cordova:"
find . -type f | xargs grep Cordova
echo "find . -type f | xargs grep device_info_plus:"
find . -type f | xargs grep device_info_plus
echo "find . -type f | xargs grep DKImagePickerController:"
find . -type f | xargs grep DKImagePickerController
echo "find . -type f | xargs grep DKPhotoGallery:"
find . -type f | xargs grep DKPhotoGallery
echo "find . -type f | xargs grep FBAEMKit:"
find . -type f | xargs grep FBAEMKit
echo "find . -type f | xargs grep FBLPromises:"
find . -type f | xargs grep FBLPromises
echo "find . -type f | xargs grep FBSDKCoreKit:"
find . -type f | xargs grep FBSDKCoreKit
echo "find . -type f | xargs grep FBSDKCoreKit_Basics:"
find . -type f | xargs grep FBSDKCoreKit_Basics
echo "find . -type f | xargs grep FBSDKLoginKit:"
find . -type f | xargs grep FBSDKLoginKit
echo "find . -type f | xargs grep FBSDKShareKit:"
find . -type f | xargs grep FBSDKShareKit
echo "find . -type f | xargs grep file_picker:"
find . -type f | xargs grep file_picker
echo "find . -type f | xargs grep FirebaseABTesting:"
find . -type f | xargs grep FirebaseABTesting
echo "find . -type f | xargs grep FirebaseAuth:"
find . -type f | xargs grep FirebaseAuth
echo "find . -type f | xargs grep FirebaseCore:"
find . -type f | xargs grep FirebaseCore
echo "find . -type f | xargs grep FirebaseCoreDiagnostics:"
find . -type f | xargs grep FirebaseCoreDiagnostics
echo "find . -type f | xargs grep FirebaseCoreExtension:"
find . -type f | xargs grep FirebaseCoreExtension
echo "find . -type f | xargs grep FirebaseCoreInternal:"
find . -type f | xargs grep FirebaseCoreInternal
echo "find . -type f | xargs grep FirebaseCrashlytics:"
find . -type f | xargs grep FirebaseCrashlytics
echo "find . -type f | xargs grep FirebaseDynamicLinks:"
find . -type f | xargs grep FirebaseDynamicLinks
echo "find . -type f | xargs grep FirebaseFirestore:"
find . -type f | xargs grep FirebaseFirestore
echo "find . -type f | xargs grep FirebaseInstallations:"
find . -type f | xargs grep FirebaseInstallations
echo "find . -type f | xargs grep FirebaseMessaging:"
find . -type f | xargs grep FirebaseMessaging
echo "find . -type f | xargs grep FirebaseRemoteConfig:"
find . -type f | xargs grep FirebaseRemoteConfig
echo "find . -type f | xargs grep Flutter:"
find . -type f | xargs grep Flutter
echo "find . -type f | xargs grep flutter_inappwebview:"
find . -type f | xargs grep flutter_inappwebview
echo "find . -type f | xargs grep flutter_local_notifications:"
find . -type f | xargs grep flutter_local_notifications
echo "find . -type f | xargs grep fluttertoast:"
find . -type f | xargs grep fluttertoast
echo "find . -type f | xargs grep FMDB:"
find . -type f | xargs grep FMDB
echo "find . -type f | xargs grep geolocator_apple:"
find . -type f | xargs grep geolocator_apple
echo "find . -type f | xargs grep GoogleDataTransport:"
find . -type f | xargs grep GoogleDataTransport
echo "find . -type f | xargs grep GoogleSignIn:"
find . -type f | xargs grep GoogleSignIn
echo "find . -type f | xargs grep GoogleToolboxForMac:"
find . -type f | xargs grep GoogleToolboxForMac
echo "find . -type f | xargs grep GoogleUtilities:"
find . -type f | xargs grep GoogleUtilities
echo "find . -type f | xargs grep grpcpp:"
find . -type f | xargs grep grpcpp
echo "find . -type f | xargs grep GTMAppAuth:"
find . -type f | xargs grep GTMAppAuth
echo "find . -type f | xargs grep GTMSessionFetcher:"
find . -type f | xargs grep GTMSessionFetcher
echo "find . -type f | xargs grep hermes:"
find . -type f | xargs grep hermes
echo "find . -type f | xargs grep image_picker_ios:"
find . -type f | xargs grep image_picker_ios
echo "find . -type f | xargs grep IQKeyboardManager:"
find . -type f | xargs grep IQKeyboardManager
echo "find . -type f | xargs grep IQKeyboardManagerSwift:"
find . -type f | xargs grep IQKeyboardManagerSwift
echo "find . -type f | xargs grep Kingfisher:"
find . -type f | xargs grep Kingfisher
echo "find . -type f | xargs grep leveldb:"
find . -type f | xargs grep leveldb
echo "find . -type f | xargs grep Lottie:"
find . -type f | xargs grep Lottie
echo "find . -type f | xargs grep MBProgressHUD:"
find . -type f | xargs grep MBProgressHUD
echo "find . -type f | xargs grep nanopb:"
find . -type f | xargs grep nanopb
echo "find . -type f | xargs grep OneSignal:"
find . -type f | xargs grep OneSignal
echo "find . -type f | xargs grep OneSignalCore:"
find . -type f | xargs grep OneSignalCore
echo "find . -type f | xargs grep OneSignalExtension:"
find . -type f | xargs grep OneSignalExtension
echo "find . -type f | xargs grep OneSignalOutcomes:"
find . -type f | xargs grep OneSignalOutcomes
echo "find . -type f | xargs grep OpenSSL:"
find . -type f | xargs grep OpenSSL
echo "find . -type f | xargs grep OrderedSet:"
find . -type f | xargs grep OrderedSet
echo "find . -type f | xargs grep package_info:"
find . -type f | xargs grep package_info
echo "find . -type f | xargs grep package_info_plus:"
find . -type f | xargs grep package_info_plus
echo "find . -type f | xargs grep path_provider:"
find . -type f | xargs grep path_provider
echo "find . -type f | xargs grep path_provider_ios:"
find . -type f | xargs grep path_provider_ios
echo "find . -type f | xargs grep Promises:"
find . -type f | xargs grep Promises
echo "find . -type f | xargs grep Protobuf:"
find . -type f | xargs grep Protobuf
echo "find . -type f | xargs grep Reachability:"
find . -type f | xargs grep Reachability
echo "find . -type f | xargs grep RealmSwift:"
find . -type f | xargs grep RealmSwift
echo "find . -type f | xargs grep RxCocoa:"
find . -type f | xargs grep RxCocoa
echo "find . -type f | xargs grep RxRelay:"
find . -type f | xargs grep RxRelay
echo "find . -type f | xargs grep RxSwift:"
find . -type f | xargs grep RxSwift
echo "find . -type f | xargs grep SDWebImage:"
find . -type f | xargs grep SDWebImage
echo "find . -type f | xargs grep share_plus:"
find . -type f | xargs grep share_plus
echo "find . -type f | xargs grep shared_preferences_ios:"
find . -type f | xargs grep shared_preferences_ios
echo "find . -type f | xargs grep SnapKit:"
find . -type f | xargs grep SnapKit
echo "find . -type f | xargs grep sqflite:"
find . -type f | xargs grep sqflite
echo "find . -type f | xargs grep Starscream:"
find . -type f | xargs grep Starscream
echo "find . -type f | xargs grep SVProgressHUD:"
find . -type f | xargs grep SVProgressHUD
echo "find . -type f | xargs grep SwiftyGif:"
find . -type f | xargs grep SwiftyGif
echo "find . -type f | xargs grep SwiftyJSON:"
find . -type f | xargs grep SwiftyJSON
echo "find . -type f | xargs grep Toast:"
find . -type f | xargs grep Toast
echo "find . -type f | xargs grep UnityFramework:"
find . -type f | xargs grep UnityFramework
echo "find . -type f | xargs grep url_launcher:"
find . -type f | xargs grep url_launcher
echo "find . -type f | xargs grep url_launcher_ios:"
find . -type f | xargs grep url_launcher_ios
echo "find . -type f | xargs grep video_player_avfoundation:"
find . -type f | xargs grep video_player_avfoundation
echo "find . -type f | xargs grep wakelock:"
find . -type f | xargs grep wakelock
echo "find . -type f | xargs grep webview_flutter_wkwebview:"
find . -type f | xargs grep webview_flutter_wkwebview
```

:::note info
「find . -type f | xargs grep XXX」の部分がメイン処理で、
カレントディレクトリ配下にある全てのファイルから
XXXの文字列を使用しているファイルと該当行を抽出します。
（サブディレクトリを含む）
:::

:::note warn
ファイル名およびフォルダ名にスペースが含まれていると、
「No such file or directory」のエラーが出てしまうので、
一時的にスペースの削除をお願いします。
:::

実行結果として、該当ありの場合は以下のようになります。

```text
.
.
.
find . -type f | xargs grep Starscream:
find . -type f | xargs grep SVProgressHUD:
./MyApp/UI/ViewController/MyViewController/MyViewController.swift:            SVProgressHUD.show()
.
.
.
```

<a id="#Chapter2"></a>
# 確認方法（2. 特定のAPIを使っているか）

情報の出所はここです。

https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api

Appleの指名したAPIたちが記載されています。
指定されたAPIを使っている場合、プライバシーマニフェストファイルを作成し、
情報を記載する必要があります。

:::note info
プライバシーマニフェストファイルについては、以下をご参照ください。
[Privacy manifest files | Apple Developer Documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files)

作成方法は以下などをご参照ください。
[プライバシーマニフェストファイル組み込み手順 – FANSHIPサポートガイド](https://support.fanship.jp/hc/ja/articles/26594869676825-%E3%83%97%E3%83%A9%E3%82%A4%E3%83%90%E3%82%B7%E3%83%BC%E3%83%9E%E3%83%8B%E3%83%95%E3%82%A7%E3%82%B9%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BF%E6%89%8B%E9%A0%86)

「プライバシーマニフェストファイル　作成方法」などでググると
他にも記事が出てきます。
:::

項目`Privacy Accessed API Type`を追加し、対象のAPIとその用途を記載します。
プルダウンがあらかじめ設定されているので、選択するだけでOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3758686/17d11a3e-e5d9-7b1e-7b3d-146812a120a6.png)

`Privacy Accessed API Reasons`については、
[こちら](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api)に各説明が載っているので、　
「該当するものはどれかな〜」と確認してから記載してください。


で、例の如く、
「そもそもうちのアプリってこれらのAPI使ってるんだっけ？」
の確認については、シェルコマンドを作ったのでご活用ください。

```shell
echo "find . -type f | xargs grep creationDate:"
find . -type f | xargs grep creationDate
echo "find . -type f | xargs grep modificationDate:"
find . -type f | xargs grep modificationDate
echo "find . -type f | xargs grep fileModificationDate:"
find . -type f | xargs grep fileModificationDate
echo "find . -type f | xargs grep contentModificationDateKey:"
find . -type f | xargs grep contentModificationDateKey
echo "find . -type f | xargs grep creationDateKey:"
find . -type f | xargs grep creationDateKey
echo "find . -type f | xargs grep getattrlist:"
find . -type f | xargs grep getattrlist
echo "find . -type f | xargs grep getattrlistbulk:"
find . -type f | xargs grep getattrlistbulk
echo "find . -type f | xargs grep fgetattrlist:"
find . -type f | xargs grep fgetattrlist
echo "find . -type f | xargs grep stat:"
find . -type f | xargs grep stat
echo "find . -type f | xargs grep fstat:"
find . -type f | xargs grep fstat
echo "find . -type f | xargs grep fstatat:"
find . -type f | xargs grep fstatat
echo "find . -type f | xargs grep lstat:"
find . -type f | xargs grep lstat
echo "find . -type f | xargs grep getattrlistat:"
find . -type f | xargs grep getattrlistat
echo "find . -type f | xargs grep systemUptime:"
find . -type f | xargs grep systemUptime
echo "find . -type f | xargs grep mach_absolute_time:"
find . -type f | xargs grep mach_absolute_time
echo "find . -type f | xargs grep volumeAvailableCapacityKey:"
find . -type f | xargs grep volumeAvailableCapacityKey
echo "find . -type f | xargs grep volumeAvailableCapacityForImportantUsageKey:"
find . -type f | xargs grep volumeAvailableCapacityForImportantUsageKey
echo "find . -type f | xargs grep volumeAvailableCapacityForOpportunisticUsageKey:"
find . -type f | xargs grep volumeAvailableCapacityForOpportunisticUsageKey
echo "find . -type f | xargs grep volumeTotalCapacityKey:"
find . -type f | xargs grep volumeTotalCapacityKey
echo "find . -type f | xargs grep systemFreeSize:"
find . -type f | xargs grep systemFreeSize
echo "find . -type f | xargs grep systemSize:"
find . -type f | xargs grep systemSize
echo "find . -type f | xargs grep statfs:"
find . -type f | xargs grep statfs
echo "find . -type f | xargs grep statvfs:"
find . -type f | xargs grep statvfs
echo "find . -type f | xargs grep fstatfs:"
find . -type f | xargs grep fstatfs
echo "find . -type f | xargs grep fstatvfs:"
find . -type f | xargs grep fstatvfs
echo "find . -type f | xargs grep getattrlist:"
find . -type f | xargs grep getattrlist
echo "find . -type f | xargs grep fgetattrlist:"
find . -type f | xargs grep fgetattrlist
echo "find . -type f | xargs grep getattrlistat:"
find . -type f | xargs grep getattrlistat
echo "find . -type f | xargs grep activeInputModes:"
find . -type f | xargs grep activeInputModes
echo "find . -type f | xargs grep UserDefaults:"
find . -type f | xargs grep UserDefaults
```


:::note info
「find . -type f | xargs grep XXX」の部分がメイン処理で、
カレントディレクトリ配下にある全てのファイルから
XXXの文字列を使用しているファイルと該当行を抽出します。
（サブディレクトリを含む）
:::

:::note warn
`stat`のgrepについて、`static`などの文字列もヒットしてしまうので、
過剰に抽出されてしまうことがあります。

対応としては、grep結果に対して`stat`を単語検索して、
ヒットするかを確認していただければと思います。
:::


実行結果として、該当ありは以下のようになります。

```text
.
.
.
find . -type f | xargs grep activeInputModes:
find . -type f | xargs grep UserDefaults:
./MyApp/DemoManager.swift:        let userDefaults = UserDefaults.standard
```

<a id="#Chapter3"></a>
# 確認方法（3. 特定のPrivacyデータを使っているか）

情報の出所はここです。

https://developer.apple.com/jp/app-store/app-privacy-details/

アプリまたはSDK側で特定のPrivacyデータを収集している場合に、
プライバシーマニフェストファイルを作成し、情報を記載する必要があります。
項目`Privacy Nutrition Label Types`を追加し、内容を記載します。

要するに、アプリで何かしらのユーザ情報を使ってる時は、
使ってるユーザ情報の種類と用途を教えてね、ってことです。


:::note info

`Privacyデータ`ってなんやねん、と思われたかもしれませんが、
[こちら](https://developer.apple.com/jp/app-store/app-privacy-details/)の`データの種類`のチャプターに記載されているものになります。
また、PrivacyInfo.xcprivacyファイルに記載しなくていいデータについては、
`開示が任意となるデータ`のチャプターで説明されています。

※ 個人的には、怪しいなあ、と思う項目があれば、
とりあえず記載してしまえばいいのではないかと考えています。
記載内容も選択制なので、そこまで負担ではないかなと。

:::

では、実際に書いていきましょう。
まず、`Privacy Nutrition Label Types`を作成すると、以下のような中身になっています。
![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3758686/a8c9cd22-c9d0-7a14-51a4-1dd229580161.png)

項目を解説します。

|  | 項目名 | 項目説明 | 入力内容 |
|:-:|:-|:-|:-|
|  1 |  Collected Data Type | Privacyデータの種類  | Nameなど<br>（選択式）  |
|  2 |  Linked to User | ユーザと紐づいているか | YES or NO の二択  |
|  3 |  Used for Tracking | トラッキングに使うか  | YES or NO の二択  |
|  4 |  Collection Purposes | 用途  | 6つの選択肢から選択(※1）  |

※1 [こちら](https://developer.apple.com/jp/app-store/app-privacy-details/)の`データの使用`のチャプターに記載されているものになります。

例えば、「メールアドレス」と「詳細な位置情報」を使用している場合の設定は
以下のような感じになります。
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3758686/8cc63110-a2fc-cdd3-f1bb-57b2f6165c49.png)

<a id="#Chapter4"></a>
# 確認方法（4. App Tracking Transparencyフレームワークを使っているか）

情報の出所はここです。

https://developer.apple.com/documentation/bundleresources/privacy_manifest_files

以下、抜粋（DeepL翻訳）です。

```
NSPrivacyTracking	
	アプリまたはサードパーティSDKが、App Tracking Transparencyフレームワークの下で
 　　定義されたトラッキングのためにデータを使用するかどうかを示すブール値。
	詳細については、ユーザーのプライバシーとデータ使用を参照してください。
		
NSPrivacyTrackingDomains	
    アプリまたはサードパーティSDKが接続する、トラッキングを行うインターネットドメインの
    一覧を示す文字列の配列。
    ユーザーがApp Tracking Transparencyフレームワークを通じてトラッキング許可を与えていない場合、
    これらのドメインへのネットワークリクエストは失敗し、アプリはエラーを受け取ります。
    NSPrivacyTrackingをtrueに設定する場合は、
    NSPrivacyTrackingDomainsに少なくとも1つのインターネット・ドメインを指定する必要があります。
 ```

要するに、`App Tracking Transparencyフレームワーク`を使っているかどうかが全てです。
以下が対応方法になります。

＜`App Tracking Transparencyフレームワーク`を使っている場合＞
- `Privacy Tracking Enabled`を追加し、`YES`を選択する。
- `Privacy Tracking Domains`を追加し、ドメインを記載する。

＜`App Tracking Transparencyフレームワーク`を使っていない場合＞
- `Privacy Tracking Enabled`のみを追加し、`NO`を選択する。

:::note info

公式QAの以下のスレッドによれば、
Privacy Manifestsに設定する値が何もない場合、
空のPrivacyInfo.xcprivacyファイルを作成するなという指示があります。
[IS PrivacyManifest.xcprivacy still… | Apple Developer Forums](https://developer.apple.com/forums/thread/746481?answerId=782256022#782256022)

そこから派生して、`Privacy Tracking Enabled`を`NO`にする場合は、
`Privacy Tracking Domains`の項目は作成不要と推測されます。

:::


:::note info

自分のアプリであれば、
「`App Tracking Transparencyフレームワーク`を使っているか」
は認識しているかと思います。

しかし例えば、
「全然中身を知らないアプリのPrivacy Manifests対応の担当になった！」
なんて人のために、判断材料をご紹介します。
ATT対応には以下のような実装が必要となります。
[【時間が無い人のXcode】 | ATT対応とは？](https://utro-develop.com/att/)

結論、
`info.plist`に`Privacy - Tracking Usage Description`の項目があるかどうか
が簡単な判断基準になるかと思います。

:::

<a id="#Finally"></a>
# 終わりに

以上で、Privacy Manifests対応は完了です。
僕自身、また周囲でアプリを審査に出した人からの情報として、
Privacy Manifestsの対応内容に問題があれば、審査時にすぐメールが飛んでくるようです。

なので一番手っ取り早い対応方法は、
くどくど考えずにとりあえず審査に出してみることだと考えます。

稚拙な文章、かつ長文で疲れたかと思いますが、少しでもあなたの役に立てば幸いです。

<a id="#References"></a>
# 参考

Privacyデータの各種英語版説明

https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests

個人的にわかりやすいPrivacy Manifests対応解説①

https://appdev-room.com/swift-manifest

個人的にわかりやすいPrivacy Manifests対応解説②

https://zenn.dev/ueshun/articles/bbbfd49cc7e9bf