😨

AppStore申請バイナリが無効???

2025/01/04に公開

対応策はどれが適切なのか?

年末と年始にFlutterとSwiftUIでiOSアプリの開発をしておりました。FirebaseやREST APIは使わずにローカルデータベースとローカル通知だけ使用しました。他にはttsを使用した。

Privacy manifest filesがないとipaファイルが無効になるエラーが出るようだ?
書いてるからエラーが出ると思ったがなしで、iOS BuildしようとしたらFlutterはエラーが出た💦

失敗したことはinfo.plistに書くことをプライバシーマニフェストに書いてしまったこと。生成AIに聞きながらAppleから送られてくるメールに対応をした。

作成の方法は以下のサイトを参考にしてください。
https://support.fanship.jp/hc/ja/articles/26594869676825-プライバシーマニフェストファイル組み込み手順

Case SwiftUI

SwiftUIだと標準のモジュールしか使用しておりません。2個しか使ってないのに特別な対応いるの?

https://developer.apple.com/documentation/swiftdata/
https://developer.apple.com/documentation/avfaudio/avspeechsynthesizer

プライバシーマニフェストはこのように記載したら審査に出せた🙌

<?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>NSPrivacyAccessedAPITypes</key>
    <array>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSUserDefaults</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>CA92.1</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

こちらがプライバシーマニフェストのファイル

<?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>NSPrivacyAccessedAPITypes</key>
    <array>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSUserDefaults</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>CA92.1</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

info.plistの対応

🤖AIに聞いてみる

  • <?xml version="1.0" encoding="UTF-8"?>: XMLの宣言部分で、このファイルがUTF-8でエンコーディングされたXMLファイルであることを示しています。
  • <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">: ドキュメントタイプ宣言で、この文書が1.0バージョンのAppleのプロパティリスト (plist) 形式に準拠していることを示しています。
  • <plist version="1.0">: ルート要素のplistタグで、バージョンが1.0であることを示しています。
  • <dict>: 辞書型のデータ構造を表すタグです。
  • <key>NSPrivacyAccessedAPITypes</key>: プライバシーアクセス権限に関する情報が含まれている部分を示すキーです。
  • <array>: 複数のプライバシーアクセス権限が並んでいることを示しています。
  • <dict>: 1つのプライバシーアクセス権限の詳細を示す辞書型のデータです。
  • <key>NSPrivacyAccessedAPIType</key>: プライバシーアクセス権限の種類を示すキーです。
  • <string>NSUserDefaults</string>: ここでは、NSUserDefaultsというAPIにアクセスすることを示しています。
  • <key>NSPrivacyAccessedAPITypeReasons</key>: そのAPIアクセスの理由を示すキーです。
  • <array><string>CA92.1</string></array>: CA92.1という理由コードで、その理由の詳細は開発者が把握している必要があります。

このようにプライバシーマニフェストでは、アプリがどのAPIにアクセスするのかとその理由を明示的に定義する必要があります。Appleの審査では、この定義内容が適切かどうかをチェックします。

参考リンクとしては、Appleの公式ドキュメントをおすすめします。特に以下のページが参考になるでしょう:

ご不明な点がありましたら、随時ご質問ください。

やはり読んで理解するしかないか(^^;;
してなくても審査に通る人いると聞いたが?
今はエラー出るんだな。。。。

Case Flutter

Flutterアプリの方はこんな技術構成です。

  • drift
  • camera
  • image_picker
  • flutter_local_notifications

Appleからこんなメールが来る?
でも無視していいらしい?
FCM使ってないからね。ローカル通知とpush通知は違うらしい。

ローカル通知の実装したらこんなメールが来る。しかし無視して良い。 
flutter_local_notificationsを使用した。

Hello,

We noticed one or more issues with a recent delivery for the following app:

MyShoppingCameraApp
Version 1.0.0
Build 9
This is for your information only and doesn't require any action:

ITMS-90078: Missing potentially required entitlement - Your app, or a library that’s included in your app, uses Apple Push Notification service (APNs) registration APIs, but the APS Environment Entitlement isn’t included in the app signature’s entitlements. If your app uses APNs, make sure the App ID is enabled for push notifications in Certificates, Identifiers & Profiles, sign the app with a distribution provisioning profile that includes the APS Environment Entitlement, and upload a new build to ensure that push notifications function as intended. For details, visit: https://developer.apple.com/documentation/usernotifications/registering-your-app-with-apns.

このアプリはBundleIDをタイポしてることを指摘されて作り直して審査に出しました💦
アプリ名にShopingと書いてたが、Shoppingが正しかった。

カメラやローカル通知の説明を詳しく記載する必要があるようだ。でない文句言われる💦

<?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>CADisableMinimumFrameDurationOnPhone</key>
    <true/>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleDisplayName</key>
    <string>My Shopping Camera</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>com.自分の名前かブログ名.myShoppingCameraApp</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>my_shopping_camera</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0.0</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>NSCameraUsageDescription</key>
    <string>商品の写真を撮影してショッピングリストに保存します。撮影した商品写真により、お買い物時に視覚的に確認できます。</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>既存の商品写真をショッピングリストに追加するためにフォトライブラリにアクセスします。</string>
    <key>NSUserNotificationAlertStyle</key>
    <string>alert</string>
    <key>NSUserNotificationUsageDescription</key>
    <string>お買い物の時間をお知らせするために通知を使用します。</string>
    <key>UIApplicationSupportsIndirectInputEvents</key>
    <true/>
    <key>UIBackgroundModes</key>
    <array>
      <string>fetch</string>
    </array>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>UISupportedInterfaceOrientations</key>
    <array>
      <string>UIInterfaceOrientationPortrait</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
      <string>UIInterfaceOrientationPortrait</string>
    </array>
    <key>com.apple.security.files.user-selected.read-only</key>
    <true/>
  </dict>
</plist>

プライバシーマニフェストの記載で結構詰まった📝

<?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>NSPrivacyAccessedAPIType</key>
    <array>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>UserDefaults</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>CA92.1</string>
            </array>
        </dict>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>PhotoLibrary</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>PH612.1</string>
            </array>
        </dict>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>Camera</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
                <string>CA614.1</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

ローカルDBや画像のアップロードをする場合の対応を生成AIに聞いてみた。

はい、Flutterアプリで drift、camera、image_pickerを使用する場合の対応について説明させていただきます。

まずはプライバシーマニフェストの設定について説明します。
プライバシーマニフェストには、アプリがアクセスするAPIとその使用目的を記述する必要があります。
このサンプルの場合、以下のようなAPIアクセスが定義されています:

  • UserDefaults: CA92.1という目的で使用
  • PhotoLibrary: PH612.1という目的で使用
  • Camera: CA614.1という目的で使用

UserDefaultsは一般的なユーザー設定の保存に使用されるAPIです。
PhotoLibraryとCameraは、画像の取り扱いに関連するAPIです。

Flutterでは、これらのAPIにアクセスするために、対応するプラグインを使用します。
drift は SQLiteデータベースにアクセスするためのプラグイン、
camera と image_pickerは、それぞれデバイスのカメラとフォトライブラリにアクセスするためのプラグインです。

これらのプラグインを使用する際は、プライバシーマニフェストの設定が重要になります。
Appleの審査では、マニフェストの内容が適切かどうかを確認するため、上記のようなプライバシー関連の設定を正しく行う必要があります。

また、これらのプラグインを使用する際は、ユーザーの許可を得る処理も必要になります。
カメラやフォトライブラリにアクセスする場合は、ユーザーに対して適切な説明を行い、許可を得る必要があります。

プライバシーマニフェストとユーザー許可の両面から、適切な実装を行えば、Flutterアプリでもカメラや画像の取り扱いができるはずです。
ご不明な点がありましたら、随時ご質問ください。

UserDefaultsとはSwiftでローカルのストレージに情報を保存するときに使うキーと値のペアのモジュールです。

最後に

iOSアプリの審査は、Androidより楽だなと思っていたのですがそうもいきませんでした😭
知見のある方に聞いたり技術記事を読んでアプリを作って申請してみないと何がダメなのかわからないですね💦

📎他にも気になって記事のリンクを貼っておく
https://qiita.com/propagandazm/items/b553cb3d4dd8214def08
https://techblog.openwork.co.jp/entry/ios-privacymanifest
https://dev.classmethod.jp/articles/support-privacy-manifests-for-ios-app/
https://zenn.dev/akatsuki174/articles/df5f2323659db7

Discussion