背景:不经过 App Store,也能修东西

某 AI 日记应用是 Expo / React Native 应用,iOS 客户端是主要载体之一。发版有两条路:走 App Store 审核、更新整个 App;或 OTA(Over-The-Air)热更新,只更新 JS 代码与资源,不重新走完整商店流程。

OTA 价值很直接:修文案、调界面细节、改一段 JS 逻辑,不用等审核、不用用户去商店手动更新,下次打开就能拿到新内容。但 OTA 也有另一面:更新包在传输或分发中被篡改,客户端拿到的就不是开发者发的那一份;版本管理混乱,还可能拿到错误版本或回退旧版。热更新越方便,对“更新包可信、版本正确”的要求就越高。

2026-10-17 的两笔提交围绕这件事:(feat(client,ios): OTA EdgeOne acceleration, signed updates, About version display & bump build to 4)、(feat(ios,ota): bump build to 5, fix Metro native updates resolution, and ensure App Store compliance)。前者把 OTA 加速、签名更新与版本展示放一起做,后者修 Metro 原生更新解析,并明确保证 App Store 合规。

本次草稿依据提交记录与部署手册、README,未逐行核对实现。按提交记录,签名机制的具体实现未在本次草稿中核对。

OTA 的价值与边界:只动 JS 层,不动原生壳

要理解 OTA,先分清应用的两层:一层是原生壳,包括原生代码、依赖、权限声明、图标与启动配置;另一层是 JS 业务代码与资源,包括界面、逻辑、图片与文案。OTA 更新的是后者,不动前者。适合的场景是 JS 逻辑改了、文案改了、资源换了、纯 JS bug 修了;不适合的是加新原生模块、改原生权限、换原生 SDK、改需重新编译的原生代码——这些必须走完整 App 发版。

README 有句话值得记:pnpm client:export:native 只导出原生 JS / 资源,不等于签名 App 或真机验收。导出只是把 JS 与资源打包出来,离“能装到真机、能通过签名的 App”还很远。OTA 更新包生成与 App 签名打包是两回事,不能把导出成功等同于发版就绪。

风险:中间人篡改与版本错乱

第一个风险是篡改。更新包在传输中被中间人替换,客户端不知情应用了被改过的 JS,后果比“更新失败”严重得多——JS 能直接影响应用行为,篡改的更新包等于给攻击者打开改应用逻辑的通道。更新包不能是“谁给都行”,必须是“能证明是开发者发的才行”。

第二个风险是版本错乱。客户端怎么知道该不该更新、更新到哪个版本?判断逻辑有问题,可能反复更新同一版本、跳过关键版本、或在网络异常时拿到不完整包就直接应用。版本错乱不一定有安全后果,但会让体验不可预测:有人拿到新版,有人停在旧版,有人在两版间横跳。

第三个风险是回源与分发不稳定。更新包要从源站发出,经加速网络送到各地客户端。回源路径不稳、缓存策略不对,客户端可能拿到过期包,或高峰时下载很慢、反复失败。OTA 体验不只是“能不能更新”,还包括“下得快不快、稳不稳”。

签名更新:先验签,再应用

签名更新核心思路是:服务端对更新包签名,客户端应用前先验签,通过才应用,失败就拒绝。这把“信任”从“链路可信”变成“内容可信”:即使传输链路被干扰,只要签名对不上,客户端就不会应用被篡改的包。签名像给更新包盖了“开发者认可”的章,客户端不认“谁送来的”,只认“章是不是真的”。

按提交记录,签名机制的具体实现未在本次草稿中核对,故不编造签名算法或密钥管理细节。设计层面仍需回答的问题是:密钥如何管理、签名覆盖整个包还是关键文件、验签失败后静默拒绝还是提示用户、旧版客户端遇到新签名格式如何兼容——这些落地时必须回答,本次不做超出提交记录的断言。

其中一次提交还提到 About 版本展示(About version display),这是容易忽略却重要的配套:用户与开发者都要能看到“当前跑的是哪个版本、哪个构建号”。没有版本展示,OTA 出问题时很难定位;有了它,排查有了锚点,版本错乱也更容易被发现。

EdgeOne 加速回源:让更新包下得更快、更稳

签名解决“可信”,EdgeOne 解决“快与稳”。 标题里 OTA EdgeOne acceleration 与 signed updates 并列,说明两者是同一批工作。部署手册记载:源站域名 供 Vercel 与 EdgeOne 外部回源使用。回源是边缘节点无缓存内容时回到源站去取;EdgeOne 把更新包缓存到离用户更近的节点,让下载更快,源站则是回源时要找的真实来源。

注意点有三:回源域名要明确,不能与对外域名混淆;缓存策略要与版本语义对齐,不能把旧版包当新版发,也不能让新版包迟迟不生效;加速只是让分发更快更稳,不能替代签名——即使包从最近边缘节点拿到,客户端仍要验签,因为“离得近”不等于“可信”。完整链路是:开发者发布并签名更新包 → 放到源站 → EdgeOne 加速分发 → 客户端下载 → 验签 → 通过后应用,每步各有职责。

Metro 原生更新解析的坑与合规边界

其中一次提交还提到 fix Metro native updates resolution。Metro 是 React Native 打包器,负责把 JS 与资源打包成客户端可运行的 bundle,OTA 更新包的生成与解析都依赖它对“哪些是原生更新、哪些是 JS 更新”的正确处理。解析出错可能表现为“更新了但没生效”“更新后白屏”“版本显示对不上”,而不一定直接报错。排查 OTA 问题时,不要只看下载与验签,也要看打包侧解析是否正确。

标题还明确写了 ensure App Store compliance:OTA 只能更新 JS 代码与资源,不能改原生代码,不能绕过 App Store 审核。不能用 OTA 上架本应经审核的新功能、改原生权限或原生行为、或分发违反商店政策的内容。商店对热更新的容忍,建立在“只改 JS 层、不动原生层”的前提上;越过这条线就可能面临下架或审核被拒。签名、版本、About 展示不只是为体验与安全,也是为让热更新行为可追溯、可说明。

小结

OTA 是双刃剑:它让 JS 与资源迭代不再依赖 App Store 审核,但也把“更新包可信、版本正确、分发稳定、合规边界”的责任交给开发者。本项目 在 2026-10-17 的两笔提交里把几件事放一起做:用 EdgeOne 加速分发,用签名更新保证可信,用 About 版本展示让版本可观测,用 Metro 解析修复保证打包正确,并守住 App Store 合规边界。下一步值得核对的是签名具体实现、密钥轮换、验签失败后的客户端行为,以及 EdgeOne 缓存策略与版本号的对应关系——这些本次未逐行核对实现。


2024 | ixs.im
此情此景, [随机]吟诗一首: