前言
一个迭代好不容易开发完成了,ipa包也终于提交审核,心想终于可以放松放松身心啦😀
然鹅,可能是我想多了,AppStore审核的一波猛烈攻势正在疯狂的朝我袭来!!!
这一次的审核之路异常艰难,甚至让我觉得比当初谈恋爱还要难啊,因为我被连续拒绝,我能肿么办。。。难受!香菇!
接下来进入正题哈
第一次被拒:
Guideline 2.5.1 - Performance - Software Requirements
Your app uses or references the following non-public or deprecated APIs:
OpenCL.framework
LoggingSupport.framework
CoreKnowledge.framework
The use of non-public or deprecated APIs is not permitted on the App Store, as they can lead to a poor user experience should these APIs change and are otherwise not supported on Apple platforms.
Next Steps
It would be appropriate to revise your binary and remove any references to the non-public or deprecated APIs identified above.
If you are using third-party libraries, update to the most recent version of those libraries. If you do not have access to the libraries' source, the following command line tools can help you identify the location of problematic code:
-
The "strings" tool can output a list of the methods the library calls.
-
The "otool -ov" tool will output the Objective-C class structures and their defined methods.
看到这个原因,顿时给我整懵圈勒???
大概意思就是说:我们的工程引用了苹果未公开(私有)或者已经废弃的API,然后主要就是上述提到的3个framework,接下来我立即打开工程,全局搜索了关键字,结果一目了然,一个都没有搜到,哈哈哈!!!一顿操作猛如虎,啥也没发现,白瞎勒。
然后苹果dd也很贴心,帮你找出了问题还教你怎么去解决这个问题,上述提到了两个命令工具:
通过终端命令行可以找出私有api被引用的位置:
方法1:strings检测
这个方法是我在被拒信息中看到的苹果大大建议的方法,步骤如下:
1、获取release的ipa包,打包是选择的方式为:App Store
2、将 .ipa 修改为 .zip,减压,获取到两个文件夹Payload、Symbols
3、打开命令行,cd 到 Payload 里面的 app,然后使用 strings 命令进行查找
strings - -a -arch armv7 "工程名" | grep string
还有一种命令,直接生成一个 txt 文件。
strings - -a -arch armv7 "工程名" >strings.txt
运行后会在桌面上出现一个 strings.txt 文件,文件的路径和名字随个人开发习惯
armv7是一种架构,可以换成arm64等均可执行
两种命令获取方式都没有定位到具体位置,只是告诉我们存在这个私有api
方法2:otool工具(xcode自带,可以直接在终端中使用)
前两步同方法1
terminal中 cd 到 Payload 里面的 app
然后使用如下命令
otool -L appName
otool -L appName //查看可执行程序都链接了那些库
otool -L appName | grep "xxx" //筛选是否链接了xxx库
otool -ov appName //输出Object-C类结构以及定义的方法
appName:是.app前面的名称
这个命令会列出你所有使用的系统库,检查一下给出的列表中有没有私有api的库
如果对命令不熟悉还可以使用
otool -help
去查看命令帮助文档
方法3:全局搜索(建议使用)
这个方法是我认为最直接最有效的方法!
1、 cd 到你的工程目录
2、使用全局搜索命令(注意最后要加一个点)
grep -r OpenCL . (r后面一个空格,然后键入引用库的名称,注意名称后面敲一个空格,然后键入.)
这个方法直接定位到这个私有api出现在 ./ThirdFrameworks/TKFMWK/AnychatSDK/libs/libx264.a 中。
有图有真相,一个命令就找到了工程里面具体引用私有库的位置,如下:
接下来就根据需要来升级三方库或者删除未用到的库即可;
补充一点:FLEX这个调试库可以只在debug模式下引用,在release模式(appstore包)删除,也可以解决这个问题;
好勒,原因找到了问题也解决了,心里又轻松了一丢丢,赶紧再次提交审核,go!!!
第二次被拒:
第二天早上赶紧打开手机上的AppStore Connect看看状态更新没,结果还是正在审核,有点诧异,以往每次提交一个晚上就有反馈,这次是怎么回事?我在想审核员这是审的有多仔细啊,最终经过了一天两夜状态更新了,但不是我想要的,又双叒叕给我退货了😭 我靠!!!
Guideline 2.5.2 - Performance - Software Requirements
Your app, extension, or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval, which is not in compliance with App Store Review Guideline 2.5.2 and section 3.3.2 of the Apple Developer Program License Agreement.
This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes. This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior and/or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.
The next submission of this app may require a longer review time, and this app will not be eligible for an expedited review until this issue is resolved.
问题升级了。从2.5.1升级到了2.5.2,莫不是问题越来越严重???
这次被拒的原因是:我们使用了类似热更新的代码,可以动态的修改程序功能,这会严重危及到用户安全,也是严重违背了apple的设计原则。我勒个去哟
看到这里我就想,我们自己写的代码是基本不可能犯这种错误,因为关于热更新这一块苹果是审核特别严格的,所以我们平常coding是很注意的,所以基本也能定位到是某些三方库的原因了,在此提醒大家,使用三方库时一定要谨慎小心,毕竟用起来一时爽,但是等到审核被各种拒绝,那场面大家一定经历过,哈哈哈~
不过,为了保险起见,我还是先在工程中全局搜索了审核回复的几个关键字: dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(),
第一和第二个基本是不会用的,没有搜到,2、3、4我们应该很熟悉,据我个人经验后面三个我们正常使用是绝对没啥问题的,除非做了什么骚操作那就另说了(This includes any code which passes arbitrary parameters to dynamic methods),我在工程中搜索了这些关键字,有用到的位置基本都是最常见的使用方式,所以我肯定不是这里的原因,然后我就改变方向。 首先我找出了从上次发布版本到这次提交审核之间,我们新增或者更新了哪些三方SDK,找到之后再从这些SDK下手;
怎么查看SDK是否有引用上述API呢? 我们也可以通过第一次被拒时,苹果给我们提供的方法, 这次我们使用nm命令:
nm命令可以列出一个函数库文件(.o .a .so)中的符号表。所谓符号,通常指定义出的函数,全局变量等等。它对于静态的函数库和共享的函数库都起作用。nm命令显示的信息用于调试库和可执行文件,对于一个给定的函数库,nm命令可以列出函数库中定义的所有符号,包括每个符号的值和类型。还可以给出在原程序中这个函数(符号)是在多少行定义的,不过这必须要求编译该函数库的时候加“-l”选项。
平常开发引入静态库时,经常遇到类似错误Undefined symbols for architecture x86_64: "_xxxx", referenced from: ...。此时,可以进入到静态库所在目录下,使用 nm 命令查看该架构下是否存在该符号: nm mySDK -arch x86_64 | grep '_xxxx'
使用
nm [选项] [文件列表(库文件/目标文件/可执行文件)]
有用的options:
-A 在每个符号信息的前面打印所在对象文件名称; -B 全局非初始化数据段(BBS段)的符号,其值表示该符号在bss段中的偏移; -b 全局static的符号; -C 输出demangle过了的符号名称; -D 打印动态符号,只显示动态符号,当使用动态库时很有用; -g 只显示全局符号的相关信息; -l 使用对象文件中的调试信息,为每一个符号查找和显示所在源文件及行号; -N debug用的符号; -n 按照地址/符号值来排序; -r const型只读的变量(readonly); -T 位于代码区的符号,比如函数main; -t 位于代码区的符号,一般是static函数; -u 打印出那些未定义的符号,即位于目标模块的外部符号,比如系统的printf()函数; -w 给出更多警告信息; -V nm命令版本号;
常见的符号类型:
A 该符号的值在今后的链接中将不再改变; B 该符号放在BSS段中,通常是那些未初始化的全局变量; D 该符号放在普通的数据段中,通常是那些已经初始化的全局变量; T 该符号放在代码段中,通常是那些全局非静态函数; U 该符号未定义过,需要自其他对象文件中链接进来; W 未明确指定的弱链接符号;同链接的其他对象文件中有它的定义就用上,否则就用一个系统特别指定的默认值。
举例 nm -u hello.o 显示hello.o 中的未定义符号,需要和其他对象文件进行链接。
首先cd 到库的根目录文件夹 nm -A “库名(.a库需带上.a后缀,.framework库则不需要后缀)” | grep "alloc" 在 指定库中找出哪个库文件使用了alloc函数。
nm ATAuthSDK -arch arm64 | grep 'alloc'
在ATAuthSDK这个framework中,查找arm64架构下的"alloc"符号。
上图是某三方sdk 我使用了 nm -A RangersAppLog.a 打印.a库定义的所有方法符号
找到了respondsToSelector一个引用,细看这里是在一个proxy的代理类里面调用的,然后联系上下文看到了消息转发等api的调用,判断出这里肯定不是一般的使用方式,然后苹果审核刚好也提到了相关api的非正常使用,所以在此我决定先删除这个三方库,再进行一次提交,喜大普奔,果不其然这次过审了。
总结
这次的提审之路有点漫长,前后经历了一周左右,好在问题都解决了,app也成功发布,欧耶! 在此记录app审核相关的问题,希望能帮助到有需要的同仁们。