🔎

iOSのPrivacy manifest filesのRequired reason APIが見つからない時の探し方

2024/03/25に公開

2024年5月1日から必須になるらしいiOSのPrivacy manifest filesのRequired reason APIの探し方についてです。

ここに辿り着いたということは、アプリをレビューに提出して下記のようなメールをもらって、それが、どこで使われてるかわからないということと思います。


TMS-91053: Missing API declaration - Your app’s code in the “my_app” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryDiskSpace. While no action is required at this time, starting May 1, 2024, when you upload a new app or app update, you must include a NSPrivacyAccessedAPITypes array in your app’s privacy manifest to provide approved reasons for these APIs used by your app’s code. For more details about this policy, including a list of required reason APIs and approved reasons for usage, visit: https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api.


まだ、このメールを見てないかたはとりあえずアプリを審査に提出するとこの警告がきます(2024年3月25日現在)。それが対応の大きなヒントになるでしょう。

Required reason APIがどこで使われてるかの探し方のヒントを書きます。

自分のソースをAPI名で検索する。

以下のURLにAPI名が書かれています。
https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api

NSPrivacyAccessedAPICategoryDiskSpace
NSPrivacyAccessedAPICategoryUserDefaults
NSPrivacyAccessedAPICategoryFileTimestamp
NSPrivacyAccessedAPIType
ごとにAPI名が書かれているのでソースを検索します。
Objective CとSwiftでAPI名が異なるものもあるので注意しましょう。

バイナリにnmコマンドを使ってシンボルから探す。

ソースコードが呼び出すライブラリからRequired reason APIが呼び出される場合は、ソースコードをAPI名で検索しても出てきません。
ipaファイルの中身をみたいのですが、ipaファイルについて詳しくは下記など。
https://www.micss.biz/2023/02/20/5857/

ターミナルからアプリ名となっているexecファイルをnmします。
nm バイナリファイル名
すると、利用しているシンボル情報が全て表示されているので、文字列検索するとそのAPIが使われているかがわかります。
下記はObjective Cで空のプロジェクトをnmした結果ですが、もちろんrequired reason APIは含まれていません。

$ nm light
                 U _NSStringFromClass
                 U _OBJC_CLASS_$_UIResponder
                 U _OBJC_CLASS_$_UISceneConfiguration
                 U _OBJC_CLASS_$_UIViewController
                 U _OBJC_METACLASS_$_NSObject
                 U _OBJC_METACLASS_$_UIResponder
                 U _OBJC_METACLASS_$_UIViewController
                 U _UIApplicationMain
                 U ___CFConstantStringClassReference
0000000100000000 T __mh_execute_header
                 U __objc_empty_cache
                 U _objc_alloc
                 U _objc_autoreleasePoolPop
                 U _objc_autoreleasePoolPush
                 U _objc_autoreleaseReturnValue
                 U _objc_claimAutoreleasedReturnValue
                 U _objc_msgSend
                 U _objc_msgSendSuper2
                 U _objc_opt_class
                 U _objc_release_x20
                 U _objc_release_x21
                 U _objc_release_x22
                 U _objc_retain_x3
                 U _objc_storeStrong
$

下のスクショはipaファイルの一例で、アプリ名のlightがバイナリとなります。


ipaファイルの中身

IPAファイル内のFrameworkのPrivacyInfo.xcprivacyから探す。

上記スクショのGoogleMobileAds.frameworkのPrivacyInfo.xcprivacyを見ると、以下のように書いてあります。何を使っているかとその理由が書かれているので参考になります。

	<array>
		<dict>
			<key>NSPrivacyAccessedAPITypeReasons</key>
			<array>
				<string>35F9.1</string>
			</array>
			<key>NSPrivacyAccessedAPIType</key>
			<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
		</dict>
		<dict>
			<key>NSPrivacyAccessedAPITypeReasons</key>
			<array>
				<string>CA92.1</string>
			</array>
			<key>NSPrivacyAccessedAPIType</key>
			<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
		</dict>
		<dict>
			<key>NSPrivacyAccessedAPITypeReasons</key>
			<array>
				<string>E174.1</string>
			</array>
			<key>NSPrivacyAccessedAPIType</key>
			<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
		</dict>
	</array>

SystemBootTime、UserDefaults、DiskSpaceの3カテゴリに関するのAPIが使われていることがわかります。

このようにframeworkがPrivacyInfoに対応済みであれば確認できます。
(対応済みでない場合は、framework作成元が対応する必要があります。)

疑問点:動的frameworkで使うAPIは本体で設定する必要があるか。

結論から書けば、呼び出されるAPIについては、本体バイナリに含まれてなくとも呼び出されるなら書く必要があるようです。
上記のGoogleMobileAdsを例にすれば、GoogleMobileAdsは動的ライブラリ(dynamically linked shared library)ですが、呼び出される分のRequired reason APIについては、記入する必要があるようです。

試してみた

実際に空のプロジェクトにadmobだけ追加して試してみました。
ただSwift Package ManagerでGoogleMobileAdsを追加しただけではAPIが使われないので、admobをテキトウに呼び出すプログラムを書きます。

空のObjective Cのプロジェクとを作成して、viewControllerを以下に書き換えます。

#import "ViewController.h"
@import GoogleMobileAds;
@interface ViewController ()
@property(nonatomic, strong) GADBannerView *bannerView;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [GADMobileAds.sharedInstance startWithCompletionHandler:nil];
    self.bannerView = [[GADBannerView alloc] initWithAdSize:GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(100.0)];
}
@end

これをアーカイブしipaファイル内のバイナリをnmすると以下の以下のようなシンボルが見つかります。

...
                 U _NSFileSystemFreeSize
                 U _NSFileSystemSize
...
                 U _NSUserDefaultsDidChangeNotification
                 U _OBJC_CLASS_$_NSUserDefaults
...

これらはDiskSpaceとUserDefaultsに対応するシンボルです。
しかし、System boot timeに関係するsystemUptime、mach_absolute_time()は見つかりません。

そして、これを審査に提出します。
すると以下のメールが来ました。SDKのPrivacyInfo.xcprivacyには3つ理由が書いてありますが、メールによれば2つの理由が書かれていました。nmコマンドで見た結果と同じくSystemBootTimeは使われなかったようです。


ITMS-91053: Missing API declaration - Your app’s code in the “app name” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryDiskSpace.

ITMS-91053: Missing API declaration - Your app’s code in the “app name” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryUserDefaults.


結論:動的ライブラリ側に書かれたAPIもアプリ本体に記述が必要な場合がある

GoogleMobileAdsは動的ライブラリでアプリ本体とは別のframeworkになっています。
しかし、審査に提出時のメールの警告を信じるならアプリ本体のPrivacyInfoに呼び出されていると思われるAPIに対応するこの場合2つのAPIの理由を書く必要がありそうです。
(確かめられないのですが、GoogleMobileAdsの呼出方法によっては、もう一つのAPIのSystem boot timeも説明が必要になるケースがあるのでしょうか?)

収集するデータの種類との記述場所の違い

なお、Required reason APIとは異なり、収集するデータの種類(NSPrivacyCollectedDataTypes)については以下の記述があります。

重要: サードパーティSDKは、収集するデータの種類を記録する自身のプライバシーマニフェストファイルを提供する必要があります。アプリのプライバシーマニフェストファイルでは、アプリがリンクするサードパーティSDKによって収集されたデータをカバーする必要はありません。
Describing data use in privacy manifestsのImortant

よって、収集するデータの種類についてサードパーティSDK側のマニフェストに書かれている場合は、アプリ側ではなくサードパーティSDK側に記述するようです。

(残りのNSPrivacyTrackingDomains、NSPrivacyCollectedDataTypesについてはどっちに書くのか現時点で私は知らない。)

静的ライブラリの話題

ちなみに、静的ライブラリについてもXcode 15以降、プライバシーマニフェストファイルを含むリソースをバンドルをできるようです。Privacy manifest filesのnote参照
ただし、Require reason APIの記述では、

アプリが必要とするAPIを使用する各実行可能ファイルまたはダイナミックライブラリについて、その実行可能ファイルまたはダイナミックライブラリを含むバンドルには、そのAPIを報告するプライバシーマニフェストファイルを含める必要があります。
Describing use of required reason APIの2つ目のimportantの上

とあります。
よって、静的ライブラリでプライバシーマニフェストがある場合に、
①収集するデータの種類: 静的ライブラリ内に記述できる
②Required reason API: それを利用している動的ライブラリや本体に記述する
と推測します。

チェック用スクリプト

ipaファイル内の実行ファイルからRequired reason APIを見つけるスクリプトを作りました。
理由に対応するAPIがどれかがわからない時にご利用ください。
https://github.com/nekomimimi/check_ios_required_reason_api_for_privacy_manifest

※上記の文章は信憑性と正確性を保証しません。自己責任でご利用ください。

Discussion