夜雨聆风学习资料网

ARTICLE · 1350929

iOS过审不是靠运气:一个生活服务App上架前踩过的8个坑

iOS过审不是靠运气:一个生活服务App上架前踩过的8个坑

很多团队第一次做 iOS 上架,会把它理解成“打个包、传到 App Store Connect、等审核”。

真正跑一遍才发现,苹果审核看的不是单一功能,而是整套产品事实是否一致:App 怎么登录、核心功能是什么、为什么需要这些权限、有没有账号注销、支付对应实物还是虚拟权益、第三方 SDK 有没有如实披露、审核员能不能真的走完全流程。

iOS 的规范还是要详细看完核对好再申请上线,因为一旦不过,后面审核就越来越严格。宁愿把提交时间往后压,也不要带着“先试一下”的侥幸心态进审核队列。

这篇复盘的是一个生活服务类 Flutter App:内容浏览、实物商城、本地生活、任务积分都在一个客户端里。下面这些坑,有的已经造成补材料,有的在提审前被及时拦住。它们都不神秘,但每一个都足够拖慢一次发版。

一、第一次提审被要求补材料,2.1 不一定是硬拒

第一次提审后,苹果返回了 Guideline 2.1 - Information Needed。

这类问题的重点是“信息不足”,不一定意味着功能违规,也不一定要重新传包。审核员通常希望你进一步说明:

  • App 的用途、目标用户和主要功能;
  • 如何登录,是否提供审核账号;
  • 核心业务流程怎么操作;
  • 是否支持账号注销;
  • 使用了哪些外部服务和第三方 SDK;
  • 是否存在地区差异、受限行业或版权内容。

如果只回复“我们的 App 可以正常使用”,基本没有帮助。审核员需要的是清晰、可执行、可验证的信息。

更好的做法是在提审前准备一份“审核员材料包”:

1. 一段清楚的 App 定位说明;

2. 稳定的审核账号和登录步骤;

3. 真机录屏,从 App 图标冷启动开始;

4. 覆盖登录、内容浏览、商城、本地生活、支付拉起、账号注销等主链路;

5. 第三方 SDK 清单及用途;

6. 英文审核备注,避免审核员猜流程。

录屏不要只录 happy path。账号注销、重新注册、异常返回、支付取消、权限拒绝后的表现,都应该提前验证。

二、审核账号能登录,不等于审核员能按你的步骤登录

最典型的一个坑,是短信登录的审核账号。

App 当时只保留验证码登录。审核员无法接收发到指定手机号的真实短信,于是后端为审核手机号配置了固定验证码。

研发本地测试时,固定验证码确实有效。但审核员操作时多了一个很自然的动作:点击“获取验证码”。

这一下就暴露出问题:

  • 有的实现是“不点发送才能通过”;
  • 有的是随机验证码覆盖了固定验证码;
  • 有的是校验接口只认缓存里的随机码;
  • 还有滑块验证、发送频控、注销后重建等链路没有一起放行。

结果就是:我们以为账号可用,审核员却登录失败。

不要在审核备注里要求审核员“请不要点击获取验证码”。这不符合正常用户路径,也很容易再次翻车。

正确方案是二选一:

  • 白名单手机号点击发送后,短信通道真的下发固定验证码;
  • 或者校验接口对白名单手机号无条件接受固定验证码,不受随机码状态影响。

验证时必须严格模拟审核员路径:输入手机号 → 点击获取验证码 → 完成滑块 → 输入固定验证码 → 登录 → 注销 → 用同一号码重新注册。

审核账号不是“能登录一次”就够,而是要能被陌生人按照页面提示顺利操作。

三、积分、提现、赚钱文案,是 iOS 的高敏区

这个 App 有任务积分体系,用户完成内容浏览、签到或任务后获得积分,部分业务还涉及提现。

在安卓环境里,团队可能更关注功能是否闭环;到了 iOS,产品表述必须更谨慎。“看内容得奖励”“积分可提现”“赚钱”“红包”等文案,容易让审核员关注 Guideline 3.2 相关风险。

这里不能简单理解成“把按钮藏起来”。苹果审核看的是完整产品形态:

  • App Store 应用描述怎么写;
  • 截图里有没有提现、赚钱表达;
  • 首页有没有奖励浮层;
  • 我的页面有没有提现入口;
  • 账号与安全里有没有收款、实名认证相关模块;
  • 协议列表里有没有提现规则;
  • WebView 标题、通知、深链、历史记录里会不会露出相关内容。

当时的处理方式是为 iOS 增加独立功能开关,并且默认隐藏高风险入口,包括提现按钮、奖励浮层、收款设置、实名认证模块及相关协议入口。开关接口失败、字段异常或客户端状态不完整时,也按隐藏处理。

同时统一产品口径:不强调赚钱,不承诺固定收益,把积分表述为会员积分或会员权益,强调实物电商和本地生活服务,说明第三方支付对应的是实物商品或线下服务,而不是购买可提现的虚拟资产。

这不是靠文案“钻空子”,而是让 iOS 版本真正呈现一套适合 App Store 规则的功能集合。产品、运营、研发、法务和提审材料必须使用同一套口径。

四、微信回跳失败,往往不是微信 SDK 一个地方的问题

另一个很耗时的坑,出现在微信授权和 Universal Link 上。

现象是:用户从 App 拉起微信,授权完成后没有正常回到 App,而是跳到一个 H5 页面,甚至出现 Safari 与 App 之间反复弹回。

Universal Link 至少涉及五个环节:

1. 微信开放平台登记的 Universal Link;

2. App 客户端配置的 Universal Link;

3. 服务端提供的 AASA 文件;

4. iOS 工程的 Associated Domains 能力;

5. App 收到回跳后的实际处理逻辑。

任何一个地方不一致,都可能失败。例如:

  • AASA 里的 Team ID 或 Bundle ID 与当前签名不一致;
  • AASA 路径没有覆盖微信实际回跳地址;
  • Debug 包需要开发者模式验证,Release 包又走 Apple CDN 缓存;
  • 微信开放平台仍是旧域名,客户端已经换成新域名;
  • 服务端 AASA 已更新,但 CDN 还没刷新;
  • App 能被拉起,却没有正确消费授权链接。

这里还有一个容易忽略的生命周期问题:当 App 使用 Scene 生命周期后,系统不一定再把 Universal Link 事件交给 AppDelegate。

冷启动和热启动的入口也不同:App 未运行时,回跳信息可能在 Scene 的 willConnectTo 中;App 在后台时,才可能通过 continue 或打开 URL 的回调进入。只实现热启动处理,会漏掉冷启动授权。

所以不能只测“App 已经打开时能不能回来”。必须覆盖未启动 App、App 在后台、Debug、Adhoc、Release 三种包,以及授权后取消、重复授权、网络异常和登录态失效。

五、隐私合规不是补一段政策,声明必须和代码一致

隐私问题最忌讳“政策里写一套,代码里做另一套”。

这次排查中就发现过明显不一致:隐私政策曾经写着不使用第三方支付,但工程实际上已经接入了微信 SDK,用于实物商城支付、授权或收款确认。

这种问题一旦被审核员发现,很难解释成技术疏忽。

iOS 侧重点核对四类内容。

第一是 IDFA 和 ATT。只要用于广告归因或跨 App 追踪,就需要先通过 ATT 获取授权。用户拒绝后,不能把全零 UUID 当成有效 IDFA,后端也要接受空值。ATT 弹窗不能早于自家隐私协议。

第二是权限文案。相机、相册、定位、通知等权限,需要说明具体使用场景,不能过于笼统,也不能诱导用户授权。

第三是隐私清单。需要结合 App 实际使用的 Required Reason API、追踪状态、追踪域名和第三方 SDK 情况核对。不是文件存在就代表合规,里面的理由必须能对应真实代码。

第四是 SDK 初始化时机。用户同意隐私政策前,不应提前初始化追踪、统计、性能监控或推送相关能力;用户拒绝后,也不能继续采集。

建议每次提审前同时做三件事:依赖清单扫描,确认实际接入了哪些 SDK;运行时抓包验证,确认同意前、拒绝后有没有数据发出;隐私政策校对,确认 SDK 名称、用途、收集字段、第三方主体是否一致。

隐私政策更新后,还要同步更新版本标识。否则老用户已经同意过旧版本,不会重新看到新的确认弹窗。

六、测试配置进入 Release,是最不值但最常见的事故

移动研发早期为了联调方便,经常会在工程里保留测试配置。这个项目也不例外,例如测试环境开关、测试证书兼容、TLS 主机名放行、隐藏环境切换入口、调试日志等。

如果这些配置进入正式提审包,就会出现很被动的问题:

  • App 默认请求测试环境;
  • 测试证书不匹配,却被客户端放行;
  • 设置页可以切换环境;
  • 日志里打印 token、手机号或用户标识;
  • 固定验证码、测试支付参数还在生产包中;
  • 内部域名、IP、调试入口可以被扫描出来。

这类问题不应该只靠提醒,而应该通过构建配置强制隔离。Release 包应显式使用生产环境、禁止运行时环境覆盖、关闭测试 TLS 放行、关闭环境切换入口、关闭敏感调试日志,并不保留固定验证码和测试支付参数。

正式包构建完成后,还要检查产物中的实际配置,而不是只看代码。比如确认接口地址、ATS 策略、权限描述、隐私清单、SDK 列表和字符串内容。

一句“我记得打正式包时加了参数”是靠不住的。要让流水线在发现测试配置时直接失败。

七、底栏顺序和 WebView 风控,容易暴露跨端细节

有两个看似不大的问题,很能体现上线检查是否细致。

第一个是底栏顺序。为了让 iOS 用户首先看到商城和本地生活,团队调整了底部 Tab 顺序。但页面数组、索引常量、底栏文案和可见性判断最初没有全部从同一个来源生成,导致部分页面的位置和显隐状态错位。

这类问题的根因是“同一个事实被写了多份”。正确做法是把 Tab 顺序作为唯一事实源,页面、文案、索引、埋点和可见性判断都从同一个枚举或配置生成。改一次顺序,不应该要求开发者手动改四五个文件。

第二个是 WKWebView 里的风控验证。验证码 SDK 对 JS 加载和验证请求之间的时间有要求。安卓 WebView 相对慢一些,固定延迟可能刚好满足;iOS WKWebView 执行更快,同样的延迟就可能低于风控时间窗,导致验证失败。

这说明不能简单用安卓现象推断 iOS,也不要写死跨端延迟。更稳妥的方式是记录 SDK 加载时间,动态计算还需要等待多久,既满足风控要求,也不影响慢设备上的体验。

类似问题还包括小屏模式、字体放大、系统版本差异、冷启动时序和弱网场景。它们不一定每个都会造成拒审,但会影响审核员对 App 完成度的判断。

八、提审通过只是开始,版本还要能灰度、能暂停、能回退

iOS 上线不能只盯着“有没有通过审核”。

如果版本发布后出现登录异常、支付问题、启动崩溃或第三方服务故障,而客户端没有远程控制能力,就会非常被动。

提审前至少要准备:

  • TestFlight 主链路回归;
  • 最低版本和强制更新机制;
  • 远程功能开关;
  • 关键模块一键降级或停用能力;
  • 崩溃、卡顿、接口错误监控;
  • 灰度放量计划;
  • 服务端回滚和数据兼容方案;
  • 审核通过后分阶段开放,而不是一次性全量。

尤其是支付、积分、任务、推送、登录等模块,要提前确认开关粒度。不能只准备一个“杀掉整个 App”的总开关。

正式提审前,也不要向业务方承诺具体上架时间。提交时间可以控制,审核时长和审核反馈不可控。项目计划里应该预留审核补材料和修复轮次。

最后的一张上线检查表

如果只记住一份清单,可以在每次 iOS 提审前核对下面这些项:

检查项
关键问题
审核材料
App 定位、功能说明、审核备注是否清楚
审核账号
审核员按页面提示能否完整登录
真机录屏
是否从冷启动覆盖所有主流程
账号注销
是否可注销、可重新注册、状态可恢复
业务合规
积分、奖励、提现、赚钱表述是否统一
支付场景
是否对应实物商品或线下服务
通用链接
开放平台、AASA、entitlements、回跳处理是否一致
IDFA/ATT
是否先同意隐私政策,再请求追踪授权
隐私清单
API 理由、追踪状态、SDK 信息是否准确
SDK 声明
政策描述是否与实际依赖和初始化一致
Release 配置
是否关闭测试环境、TLS 放行、调试入口
构建产物
是否检查实际地址、权限、字符串和敏感信息
页面顺序
页面、索引、文案是否来自同一事实源
发布预案
是否支持灰度、远程开关、监控和回退

iOS 过审的本质,不是把功能“包装”成合规的样子,而是让产品形态、技术配置、隐私声明、提审素材和运营预案保持一致。

真正可靠的上线能力,来自提审前的证据、默认安全的配置,以及出现问题后可以快速收回的开关。

少依赖临场发挥,少重复提交,把问题前置,才是一个团队 iOS 发布能力成熟的开始。

往期

《达到什么标准就可以上线了?》

《怎样做CR》

《单元测试的最佳实践》

《意识型面试题:我们更换了证书,某行没换导致问题怎么解决?》

《编写代码的「八荣八耻」- 以开关上线为荣,以自信编码为耻》

—— 静姐 / 编程一生

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料