媒介
前段时间用Flutter开辟的iOS App提交了新版本,结果刚过两分钟就收到了一封问题邮件,我还以为审核被拒了,结果并没有。
粗略看了看,只是出了审核新政策,2024/05/01开始才会被拒。那还早,其时我就不慌了,继续忙其他事情,直到现在才有空研究一番这个问题。
留意:文中有许多形貌没有官方中文,所以可能存在翻译不敷正确的环境。
开辟环境
- Xcode: 15.3
- Flutter: 3.19.4
- macOS: 14.4
问题形貌
审核问题邮件内容:
- Although submission for App Store review was successful, you may want to correct the following issues in your next submission for App Store review. Once you've corrected the issues, upload a new binary to App Store Connect.
- ITMS-91053: Missing API declaration - Your app’s code in the “Runner” 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.
- ITMS-91053: Missing API declaration - Your app’s code in the “Runner” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryFileTimestamp. 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.
- ITMS-91053: Missing API declaration - Your app’s code in the “Runner” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategorySystemBootTime. 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.
- ITMS-91053: Missing API declaration - Your app’s code in the “Runner” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryUserDefaults. 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.
复制代码 问题分析
1. 隐私清单 & SDK签名
从邮件内容看,问题很明确,缺少API声明,看了看链接指向的文档,原来是新政策要求提供隐私清单,并声明所用API的缘故原由。
If you upload an app to App Store Connect that uses required reason API without describing the reason in its privacy manifest file, Apple sends you an email reminding you to add the reason to the app’s privacy manifest. Starting May 1, 2024, apps that don’t describe their use of required reason API in their privacy manifest file aren’t accepted by App Store Connect.
更多详情请看这个视频介绍:WWDC2023 - Get started with privacy manifests,右下角字幕可以切换为简体中文。
视频可以简朴概括两点:
- App的大部分隐私环境往往取决于第三方SDK,这就可能导致App的隐私标签不敷正确,所以要求第三方SDK也提供隐私清单用于天生隐私陈诉,资助App开辟者更正确地创建隐私标签
- 第三方SDK以二进制文件的情势依靠时要求签名,更多详情请看视频WWDC2023 - Verify app dependencies with digital signatures
补充:隐私标签的创建位于App Store Connect的App 隐私页面。
找了找隐私清单相关的官方文档:
- Describing data use in privacy manifests
- Describing use of required reason API
结合前面的视频,可知隐私清单主要包罗数据使用声明和所用API缘故原由形貌,先逐个看看都是些什么。
1.1. 隐私清单 - 数据使用声明
官方文档中的概述:
Record the categories of data that your app or third-party SDK collects about the person using the app, and the reasons it collects the data. App developers can use Xcode to create a privacy report, summarizing the information about collected data in their app and the third-party SDKs the app links to.
记录您的应用或第三方SDK收集的有关应用使用者的数据类别,以及收集数据的缘故原由。应用程序开辟职员可以使用Xcode创建隐私陈诉,总结有关应用程序中收集的数据以及应用程序链接到的第三方SDK的信息。
须要填写的内容:
KeyDescriptionNSPrivacyCollectedDataType用于标识您的应用或第三方SDK收集的数据范例NSPrivacyCollectedDataTypeLinked指示您的应用程序或第三方SDK是否将此数据范例关联到用户的身份NSPrivacyCollectedDataTypeTracking指示您的应用程序或第三方SDK是否使用此数据范例进行跟踪NSPrivacyCollectedDataTypePurposes列出您的应用或第三方SDK收集数据的缘故原由 NSPrivacyCollectedDataType列举的部分数据范例:
NSPrivacyCollectedDataTypePurposes的可选项:
不知道你有没有感觉很熟悉,如果你上架过应用,那应该不会生疏,创建隐私标签时的选项不就是这些。打开App Store Connect的App 隐私页面,找到数据范例并编辑:
上图中的选项对应着NSPrivacyCollectedDataType列举的数据范例。再看看下面这些,分别对应着NSPrivacyCollectedDataTypeLinked、NSPrivacyCollectedDataTypeTracking、NSPrivacyCollectedDataTypePurposes:
所以给App创建隐私清单后,关于数据使用声明的填写,可以参考以前创建的隐私标签,究竟以前创建的隐私标签基本没包罗第三方SDK(如果以前有包罗,填写时不须要涵盖)。下面举个简朴例子,更多填写细节可以参考前面的视频介绍:
项目打包后天生的隐私陈诉(截取部分):
如图,如果第三方SDK有填写,也会整合在隐私陈诉中,以后隐私标签的创建可以直接参考隐私陈诉。那如果不想填数据使用声明可以吗?现在可以。
一是官方文档中没有明确声明会拒绝未填写数据使用声明的应用;二是提交审核临时也没有收到这方面的问题邮件。隐私清单中的数据使用声明更多是为了资助App开辟者更正确的创建隐私标签。
1.2. 隐私清单 - 所用API缘故原由形貌
官方文档中的概述:
Some APIs that your app uses to deliver its core functionality — in code you write or included in a third-party SDK — have the potential of being misused to access device signals to try to identify the device or user, also known as fingerprinting. Regardless of whether a user gives your app permission to track, fingerprinting is not allowed. Describe the reasons your app or third-party SDK on iOS, iPadOS, tvOS, visionOS, or watchOS uses these APIs, and check that your app or third-party SDK only uses the APIs for the expected reasons.
您的应用程序用来提供其核心功能的某些API(在您编写的或包罗在第三方SDK中的代码中)有可能被滥用来访问设备信号以尝试辨认设备或用户,也称为指纹辨认。无论用户是否授予您的应用跟踪权限,都不允许进行指纹辨认。形貌您的iOS、iPadOS、tvOS、visionOS或watchOS上的应用或第三方SDK使用这些API的缘故原由,并查抄您的应用或第三方SDK是否仅出于预期缘故原由使用这些API。
须要填写的内容:
KeyDescriptionNSPrivacyAccessedAPIType用于标识您的应用程序使用的所需缘故原由API的类别NSPrivacyAccessedAPITypeReasons用于标识您的应用程序使用API的缘故原由 可选API类别及缘故原由列表:
末了更新日期:2024/03/31
NSPrivacyAccessedAPITypeNSPrivacyAccessedAPITypeReasonsNSPrivacyAccessedAPICategoryFileTimestampDDA9.1声明此缘故原由以向使用该设备的职员显示文件时间戳。
出于此缘故原由访问的信息或任何派生信息可能不会发送到设备外。C617.1声明此缘故原由以访问应用程序容器、应用程序组容器或应用程序的CloudKit容器内文件的时间戳、大小或其他元数据。3B52.1声明此缘故原由以访问用户明确授予访问权限的文件或目录的时间戳、大小或其他元数据,例如使用文档选择器视图控制器。0A2A.1如果您的第三方SDK提供文件时间戳API的包装函数供应用程序使用,而且您仅在应用程序调用您的包装函数时才访问文件时间戳API,请声明此缘故原由。此缘故原由只能由第三方SDK声明。如果您的第三方SDK主要是为了包装所需的缘故原由 API 而创建的,则可能不会声明此缘故原由。
出于此缘故原由访问的信息或任何派生信息不得用于您的第三方SDK自身的目的,也不得由您的第三方SDK发送至设备外。NSPrivacyAccessedAPICategorySystemBootTime35F9.1声明此缘故原由以访问系统启动时间,以便丈量应用程序内发生的变乱之间经过的时间量或执行盘算以启用计时器。
出于此缘故原由访问的信息或任何派生信息可能不会发送到设备外。有关应用程序内发生的变乱之间经过的时间量的信息有一个例外,该信息可能会发送到设备外。8FFB.1声明此缘故原由以访问系统启动时间,以盘算应用程序内发生的变乱的绝对时间戳,例如与UIKit或AVFAudio框架相关的变乱。
您的应用内发生的变乱的绝对时间戳可能会发送到设备外。出于此缘故原由访问的系统启动时间或从系统启动时间派生的任何其他信息可能不会发送到设备外。3D61.1声明此缘故原由以在使用设备的人选择提交的可选错误陈诉中包罗系统启动时间信息。系统启动时间信息必须作为陈诉的一部分显着地向职员显示。
仅在用户肯定选择提交包罗系统启动时间信息的特定错误陈诉之后,而且仅出于调查或响应错误陈诉的目的,才可以将出于此缘故原由访问的信息或任何派生信息发送到设备外。NSPrivacyAccessedAPICategoryDiskSpace85F4.1声明此缘故原由以向使用该设备的职员显示磁盘空间信息。磁盘空间可以以信息单位(例如字节)或与媒体范例结合的时间单位(例如高清视频的分钟)来显示。
出于此缘故原由访问的信息或任何派生信息可能不会发送到设备外。有一个例外,允许应用程序通过本地网络将磁盘空间信息发送到同一个人操作的另一台设备,仅用于显示该设备上的磁盘空间信息;此例外仅适用于用户明确允许发送磁盘空间信息的环境,而且该信息不得通过Internet发送。E174.1声明此缘故原由是为了查抄是否有足够的磁盘空间来写入文件,大概查抄磁盘空间是否不足,以便应用程序可以在磁盘空间不足时删除文件。应用程序必须根据磁盘空间以用户可观察到的方式表现差别。
出于此缘故原由访问的信息或任何派生信息可能不会发送到设备外。有一个例外,允许应用程序在磁盘空间不足时避免从服务器下载文件。7D9E.1声明此缘故原由以在使用设备的职员选择提交的可选错误陈诉中包罗磁盘空间信息。磁盘空间信息必须作为陈诉的一部分突出地显示给相关职员。
仅在用户明确选择提交包罗磁盘空间信息的特定错误陈诉之后,而且仅出于调查或响应错误陈诉的目的,因此访问的信息或任何派生信息才可以发送到设备外。B728.1如果您的应用程序是康健研究应用程序,而且您访问此API类别来检测并通知研究参与者磁盘空间不足影响研究数据收集,请声明此缘故原由。
您的应用程序必须符合应用程序商店审核指南§5.1.3。除了提供有关康健研究的信息并允许人们参与康健研究之外,您的应用程序不得提供任何其他功能。NSPrivacyAccessedAPICategoryActiveKeyboards3EC4.1如果您的应用程序是自定义键盘应用程序,而且您访问此 API 类别以确定设备上处于活动状态的键盘,请声明此缘故原由。
为用户提供系统范围的自定义键盘必须是应用程序的主要功能。
出于此缘故原由访问的信息或任何派生信息可能不会发送到设备外。54BD.1声明此缘故原由以访问活动键盘信息,以便向使用该设备的职员出现精确的自定义用户界面。应用程序必须具有用于输入或编辑文本的文本字段,而且必须以用户可观察到的方式根据活动键盘接纳差别的行为。
出于此缘故原由访问的信息或任何派生信息可能不会发送到设备外。NSPrivacyAccessedAPICategoryUserDefaultsCA92.1声明此缘故原由以访问用户默认读取和写入只能由应用程序本身访问的信息。
此缘故原由不允许读取其他应用程序或系统写入的信息,或写入可由其他应用程序访问的信息。1C8F.1声明此缘故原由以访问用户默认值以读取和写入仅可由与应用程序本身属于同一应用程序组的成员的应用程序、应用程序扩展和应用程序剪辑访问的信息。
此缘故原由不允许读取由同一应用程序组外部的应用程序、应用程序扩展或应用程序剪辑或系统写入的信息。如果系统提供来自全局域的信息,则您的应用程序不负担责任,由于当您的应用程序尝试读取应用程序的应用程序组中的应用程序、应用程序扩展或应用程序剪辑写入的信息时,您哀求的域中不存在密钥。
此缘故原由也不允许写入可由同一应用程序组外部的应用程序、应用程序扩展或应用程序剪辑访问的信息。C56D.1如果您的第三方SDK围绕用户默认API提供包装函数供应用程序使用,而且您仅在应用程序调用您的包装函数时才访问用户默认API,请声明此缘故原由。此缘故原由只能由第三方SDK声明。如果您的第三方SDK主要是为了包装所需的缘故原由API而创建的,则可能不会声明此缘故原由。
出于此缘故原由访问的信息或任何派生信息不得用于您的第三方SDK自身的目的,也不得由您的第三方SDK发送至设备外。AC6B.1声明此缘故原由以访问用户默认读取com.apple.configuration.managed键以检索MDM设置的托管应用程序设置,或设置com.apple.feedback.managed键来存储要通过MDM查询的反馈信息,如Apple移动设备管理协议参考文档中所述。 可以看到,问题邮件中提到的API在这都有列出来。按道理说照着官方文档填写就能解决当前问题了,可问题是我这是Flutter项目,业务逻辑代码编译后是放在了App.framework,Flutter项目默认的主程序文件Runner被查抄出用了这些API没声明,我咋知道它用来干啥了?
首先要确定Runner可执行文件中的这些API调用怎么来的,本身Flutter项目中的iOS工程就没写几行代码,所以严重猜疑是来自静态库(.a)或静态框架(设置了s.static_framework = true的framework)。
使用Shell脚本扫描项目,果然一堆静态框架使用了这些API。大致根据实际用途,在App的隐私清单中选择填写:
第三方SDK没有隐私清单临时先不管,重新提交审核,不再收到问题邮件!由此,临时可以得出以下两个结论:
- 现在审核主要针对主程序文件,督促App开辟者增加隐私清单
- 由于静态库和静态框架在项目构建时会被静态链接到主程序可执行二进制文件,所以这些库的所用API缘故原由形貌须要在App的隐私清单中填写
1.3. SDK签名
根据视频介绍,个人理解这个第三方SDK要求签名的查抄应该是由Xcode完成,并不是在提交审核阶段。来由如下:
- 第三方SDK签名校验允许SDK提供者自签名,App开辟者完全可以自行对第三方SDK进行签名
- Xcode打包时会用本机的证书对这些第三方SDK进行签名,所以提交审核的包不会存在没有签名的环境
- 第三方SDK如果已有签名,实测Xcode打包时也会用本机的证书重签名覆盖,所以提交审核的包不会存在原有签名的环境
可以通过以下命令检察框架的签名信息:
- codesign -d --verbose=4 <framework-path>
复制代码 当然,以上只是当前的个人理解,以后大概会有变革。
2. 即将发布的第三方SDK要求
从WWDC2023到2024年5月,苹果给了将近一年的时间给开辟者适配。时间固然足够,可是第三方SDK众多,以致有许多已经制止更新,那该怎么办?
从前面的问题邮件可以看出,临时还只是督促App开辟者填写隐私清单,并没有指出第三方SDK的问题。更告急的是有这个即将发布的第三方SDK要求:
留意:以上只截取了部分名单,诸多Flutter的库在列(Flutter库一样平常是下划线命名,iOS原生库一样平常是大驼峰命名)。
个人理解,对于第三方SDK,现在照旧以抓典型为主,不在名单内的不符合要求不一定抓。如果你用了名单内的,别担心,大部分已经适配,更新版本即可;如果不在名单内,已经适配的也只管更新版本;如果已经制止更新,那没办法只能本身手动适配。
解决方案
新建隐私清单文件:
不要忘了勾选Targets,否则不会打包进应用程序:
参考前面的问题分析填写缺失的所用API缘故原由形貌。如果你不清楚这些API是在那里调用的,可以使用Shell脚本扫描项目。关于脚本的一些使用请看:iOS - 超好用的隐私清单分析脚本(持续更新)。
末了
如果这篇文章对你有所资助,点赞 |