最近处理了一个很常见但又容易踩坑的需求:已经打好了 iOS Ad Hoc 包,怎么让测试、客户验收或演示同事通过一个外网链接安装?
先说结论:Ad Hoc 外网分发并不是绕过 Apple 的安装限制,它只是把已经签名好的 IPA 放到一个可访问、可下线、可追踪的下载链路上。设备仍然必须提前登记到 Apple Developer 账号里,IPA 也必须使用包含这些设备的 Ad Hoc provisioning profile 签名。
推荐方案
我更推荐的结构是:
1 | 静态安装页 + manifest.plist + 外部 IPA 托管 |
也就是:
1 | GitHub Pages / 静态站点 |
安装页负责展示版本信息和安装按钮,manifest.plist 负责告诉 iOS IPA 在哪里,IPA 本体则放到更适合大文件下载的地方。
如果只是临时验证,并且 IPA 小于 100 MB,也可以短时间把 IPA 放在 GitHub Pages 目录里。但这不适合作为长期方案:包体会让仓库变重,下载量稍大也会碰到带宽和仓库文件大小限制。
安装原理
iOS 网页安装 Ad Hoc 包依赖 itms-services 协议。安装页里一般放一个这样的链接:
1 | <a href="itms-services://?action=download-manifest&url=https%3A%2F%2Fexample.com%2Fios-dist%2Fcompany-app%2Fmanifest.plist"> |
注意几个细节:
url=后面的 manifest 地址必须 URL Encode。- manifest 地址必须是 HTTPS。
- manifest 里面的 IPA 地址也必须是 HTTPS。
- 尽量让用户用 iPhone 或 iPad 的 Safari 打开。
- 微信、企业 IM、邮件内置浏览器可能拦截安装动作。
一个最小可用的 manifest.plist 大概是这样:
1 |
|
目录怎么设计
分发目录最好不要只用 App 名和版本号,因为这种路径太容易猜。
我一般会加一个随机片段:
1 | ios-dist/ |
这样安装页地址可以是:
1 | https://sweetloser.com/ios-dist/company-app/1.2.3/45-a8f3c92d/install.html |
如果希望博客和分发页更干净地隔离,也可以用独立子域名:
1 | https://dist.sweetloser.com/company-app/1.2.3/45-a8f3c92d/install.html |
独立子域名的好处是后续迁移方便。今天用 GitHub Pages,明天切对象存储、Cloudflare Pages、Vercel 或自建服务,链接结构不会和博客内容绑死。
为什么 IPA 不建议直接放 Pages
GitHub Pages 非常适合放安装页、说明页、图标、manifest,但不适合作为长期 IPA 仓库。
主要原因:
- IPA 是大文件,多版本保留后仓库会很快变大。
- GitHub 普通仓库会限制大文件,超过 100 MiB 就会很麻烦。
- GitHub Pages 更像静态站点托管,不是下载分发服务。
- 对象存储和 CDN 在日志、限速、缓存、下线、生命周期清理方面更好用。
所以长期方案里,我会让 Pages 只承担入口层,IPA 放对象存储、CDN、GitHub Release 或其他文件下载服务。
外网链接的安全边界
公开链接模式要接受一个事实:拿到链接的人都能访问。
随机目录、robots.txt、noindex 只能降低被搜索和枚举的概率,它们不是权限控制。
可以做的基础措施有:
- 版本目录增加随机片段。
- 不提供公开版本列表。
- 安装页加
<meta name="robots" content="noindex,nofollow">。 robots.txt屏蔽分发目录。- 保留下载日志,关注异常 IP、异常下载量和异常 User-Agent。
- 发现链接扩散后,立刻下线旧目录,重新生成随机目录。
如果包内容敏感,或者分发对象很严格,就不要只依赖随机路径。那种场景应该加鉴权、短期签名 URL,或者使用更正式的内部分发平台。
发布前检查清单
每次发布前,我会按下面这份清单过一遍:
install.html能通过 HTTPS 访问。- 安装链接里的 manifest URL 已 URL Encode。
manifest.plist返回 200。- manifest 里的 IPA URL 返回 200。
- IPA 使用 Ad Hoc 签名。
- 目标设备 UDID 已包含在 provisioning profile 中。
- iPhone Safari 能点击安装。
- 微信或企业 IM 内置浏览器有 Safari 打开提示。
- 页面包含
noindex,nofollow。 - 分发目录已被
robots.txt屏蔽。 - 旧版本下线后,旧安装页不能继续安装。
常见安装失败原因
点击安装没有反应时,优先排查:
- 是否使用 Safari 打开。
- manifest 是否 HTTPS。
- manifest 是否返回 200。
- IPA URL 是否返回 200。
- Bundle ID、版本号、title 是否填写正确。
- 当前设备是否在 Ad Hoc 设备列表中。
- 设备上是否已有同 Bundle ID 但签名不同的 App。
如果页面和 manifest 都没问题,但仍然无法安装,最常见的原因就是设备没有被加入 Ad Hoc profile。外网分发链路只能负责下载,不能改变 iOS 的签名校验。
最后
iOS Ad Hoc 外网分发最重要的不是页面多漂亮,而是链路稳定、路径清晰、能快速下线。
我的推荐可以压缩成一句话:
1 | 安装页放静态站点,IPA 放对象存储或 CDN,路径加随机片段,发布前真机验证,下线时立刻断入口。 |
这样做足够轻,也足够稳。对于内部测试、客户验收和小范围演示,比一上来搭完整分发后台更合适。