跳转至

iOS 上架流程与构建文档

点击下载 h5_iOS_Android.zip

目录


第一部分 上架完整流程

1. 流程总览

iOS 上架和 Android 最大的区别:Android 的签名文件是你自己在本地生成的,iOS 的签名文件必须由 Apple 签发。所以 iOS 一定是「先注册付费账号 → 再申请证书 → 才能装到真机 / 上架」,没有账号连真机调试都受限。

准备资质材料
   └─ 营业执照 / 身份证、D-U-N-S 编号(公司账号)、软件著作权、隐私政策、App 备案
        ↓
注册 Apple Developer Program 账号(付费,年费制)
        ↓
账号审核通过(个人较快;公司需核验 D-U-N-S 与法人授权)
        ↓
在 Mac 上用「钥匙串访问」生成 CSR 请求文件
        ↓
Apple Developer 后台申请四件套:
   ├─ Certificates    证书(Development 开发 / Distribution 发布)
   ├─ Identifiers     App ID(Bundle ID = com.d3.app)
   ├─ Devices         测试设备 UDID(真机调试 / Ad Hoc 需要)
   └─ Profiles        描述文件(Development / Ad Hoc / App Store)
        ↓
Xcode「Signing & Capabilities」配置签名(Team + Bundle ID)
        ↓
构建 H5 产物 → 同步到 iOS → Xcode Archive
        ↓
Distribute App → 上传到 App Store Connect
        ↓
App Store Connect 填写元数据 / TestFlight 内部测试
        ↓
提交审核 → 通过 → 发布

2. 上架前需要准备的材料

材料 说明 个人账号 公司账号
身份证 / 护照 实名认证用 必须 法人 + 联系人
营业执照 公司主体资质 不需要 必须
D-U-N-S 编号 邓白氏编码,Apple 用来核验公司法人实体身份,申请公司账号的前置条件 不需要 必须(详见 3.2)
法人授权书 若申请人不是法人,Apple 可能要求提供 不需要 可能需要
软件著作权证书 中国大陆区上架游戏/部分品类需要;非游戏类通常不强制。Apple 官方文档未把软著列为必需项,国内同类平台(华为)也明确其为「非必选资质」,仅游戏品类必需(软著 + 版号) 视品类 视品类
App 备案号 中国工信部要求,中国大陆分发的 App 需完成备案;未备案会被要求下架。备案在云服务商(接入商)的备案系统办理,不在应用商店办;多个包名须逐个备案(详见 7.3) 需要 需要
隐私政策链接 必须是可公网访问的 URL,App Store Connect 里是必填项 必须 必须
应用图标 1024×1024 PNG,不能带 Alpha 通道、不能带圆角 必须 必须
应用截图 按 Apple 要求的机型尺寸提供(至少 6.7 英寸 iPhone 一组) 必须 必须
演示账号 审核员登录用的测试账号密码,产物需要登录才能进主界面时必须提供 必须 必须
支付方式 支持国际支付的信用卡(缴年费) 必须 必须

备案与资质的官方口径以工信部与 Apple 的最新要求为准。本节的备案规则以华为官方《APP 备案指引》为参考(工信部要求对各分发渠道一致),实际以你所用云服务商备案系统的提示为准。

个人账号 vs 公司账号的实际差别(决定要不要折腾 D-U-N-S):

维度 个人(Individual) 组织(Organization / 公司)
App Store 上显示的开发者名 个人姓名 公司名称
D-U-N-S 编号 不需要 必须
团队协作 只有你一个人,不能加成员 可以在 App Store Connect 里加多个成员、分配角色
部分能力 不支持企业内部分发等能力 支持更完整
审核难度 简单 需核验企业身份,周期更长

以公司/团队名义发布的应用应当走组织账号,否则 App Store 上会挂个人姓名。

3. 注册 Apple 开发者账号

3.1 注册入口与整体顺序

  1. 打开 https://developer.apple.com/
  2. 先注册一个 Apple ID(如果公司已有专用 Apple ID 就直接用,不要用个人私人 Apple ID);
  3. 给这个 Apple ID 开启双重认证(Two-Factor Authentication)——Apple 官方把「已启用双重认证的 Apple 账户」和「达到所在地区的法定成年年龄」列为加入开发者计划的两个前置条件,没开双重认证无法加入;
  4. 用这个 Apple ID 加入 Apple Developer Program(年费 99 美元,公司与个人同价;Apple Developer Enterprise Program 为 299 美元)。中国大陆开发者需要发票可致电 400-666-8800,退税事宜联系 salestax@apple.com
  5. 填写主体信息,个人选 Individual,公司选 Organization(这一步之后就要填 D-U-N-S 编号)。
  6. 🔴 组织注册的硬要求:必须是法人实体,Apple 不接受 DBA(商号)、虚构名称、假名或分支机构名称;必须有 D-U-N-S 编号;申请人必须具备约束授权(有权代表组织签署法律协议,否则需法人授权书);必须使用组织域名的工作邮箱;必须有公开可访问的官方网站,且域名与组织名称相关联。Apple 还可能额外要求提供经公证的商业文件
  7. 若以个人/独资身份注册,你的个人法定姓名会作为供应商名称显示在 App Store 上;不希望公开真名就走组织账号。
  8. 支付年费 → 等 Apple 审核 → 收到「Welcome to the Apple Developer Program」邮件即开通。
  9. 🔴 关键差异个人/独资可以在注册流程中直接购买会员资格;组织必须等 Apple Developer Support 完成身份验证并发来邮件之后,才能购买会员资格。也就是说公司账号在「提交材料」和「能付钱」之间存在一段人工等待,无法自助跳过。

强烈建议:这个 Apple ID 用公司公共邮箱注册(比如 dev@公司域名),密码和双重认证手机号做好交接记录。这个账号一旦丢了,已上架的 App 拿不回来。

README.md 里有一条经验:「Mac 账户和开发者账户保持一致」——指的是 Xcode 里登录的 Apple ID 要和申请证书的开发者账号是同一个,否则 Xcode 拉不到你的证书和描述文件。

3.2 D-U-N-S 编号(公司账号必须)

D-U-N-S(邓白氏编码)是邓白氏公司发放的全球企业唯一标识,Apple 用它来确认「这家公司真实存在,且申请人有权代表它」。Apple 官方表述为「D-U-N-S 编号在大多数司法管辖区可免费申请」,不要被第三方代办的收费误导。

申请要点:

  1. 先查是否已有:Apple 提供查询入口(https://developer.apple.com/enroll/duns-lookup/),输入公司法定名称、国家/地区搜索。很多公司其实已经被登记过了,查到就直接用,不用重新申请。
  2. 查不到再申请:在上述页面提交公司法定名称、注册地址、法人、联系电话、公司网站等。填写的公司名和地址必须和营业执照完全一致(包括括号、简繁体),不一致是最常见的失败原因。
  3. 等待:Apple 官方文档未给出承诺时长(公开资料一般说 5 个工作日左右,实际可能更久,以官方通知为准);邓白氏可能会打电话到你填的公司电话核实,务必确保这个电话能接通
  4. 拿到编号后再回到 Apple 的 Enroll 流程填进去。

3.3 注册过程中常见卡点

现象 原因 处理
Enroll 页面不让选 Organization Apple ID 没开双重认证,或年龄/地区信息不全 补全 Apple ID 信息、开双重认证
提交后一直「Pending」 Apple 在核验 D-U-N-S 与法人信息 查企业邮箱(含垃圾箱),Apple 会发邮件要补材料
Apple 要求「法人授权证明」 申请人不是法人本人 出具公司授权书(Apple 会给模板要求),法人签字盖章
付费失败 国内借记卡不支持 换支持境外支付的信用卡
公司名对不上 D-U-N-S 里登记的名称和营业执照不一致 先去邓白氏更正 D-U-N-S 信息,再回 Apple

3.4 账号内的角色说明

开通后在 App Store Connect(https://appstoreconnect.apple.com/)里管理成员,常用角色:

  • Account Holder(账号持有人):唯一,付款和法律责任主体,能做所有事;
  • Admin(管理员):能管成员、管证书、能提交审核;
  • App Manager:能管具体 App 和提交版本,不能管账号;
  • Developer:能上传构建版本,不能提交审核;
  • Marketing / Sales / Finance:只读或财务相关。

开发人员日常打包上传给 Developer 就够;证书和描述文件的管理权限尽量收在 1~2 个人手上,避免有人手滑吊销(Revoke)了正在用的发布证书。


4. 先理解 iOS 的签名体系

这一节不是流程,是概念。不理解这四个东西的关系,后面每一步都会踩坑

概念 后台位置 作用 类比 Android
Certificate(证书) Certificates 证明「这段代码是我这个开发者签的」。分 Development(开发调试)和 Distribution(分发上架) 类似 release.jks 里的密钥对,但必须 Apple 签发
Identifier(App ID) Identifiers 注册你的 Bundle ID,并声明这个 App 要用哪些能力(推送、iCloud、Sign in with Apple…) 类似 applicationId + 权限声明
Device(设备) Devices 登记允许安装非上架包的真机 UDID Android 没有对应概念
Provisioning Profile(描述文件) Profiles 把「证书 + App ID + 设备列表」打包成一个授权凭证,装进 App 里 Android 没有对应概念

关键关系:

CSR(本机生成,含公钥)
   │  上传到 Apple
   ▼
Certificate(.cer,Apple 用它证明公钥属于你)
   │  必须和本机钥匙串里的私钥配对才可用
   ├──────────────┐
   ▼              ▼
Development     Distribution
   │              │
   └──── + App ID + Devices ────┐
                                ▼
                     Provisioning Profile
                       ├─ Development  → 真机调试
                       ├─ Ad Hoc       → 发给指定设备的测试包
                       └─ App Store    → 上架 / TestFlight

最重要的一条:证书的私钥只在你生成 CSR 的那台 Mac 的钥匙串里。换电脑后即使从 Apple 后台重新下载 .cer,没有私钥也签不了包。所以:

  • 要么把证书连私钥一起导出成 .p12 文件保管好(见 5.7);
  • 要么换机后重新生成 CSR 重新申请证书(发布证书有数量上限,滥用会被卡)。

🔴 证书数量限制:Apple 官方原文为「分发证书属于团队每个团队只能有一种类型的分发证书(Developer ID 证书除外)。只有账户持有人或管理角色可以创建分发证书(如果你以个人身份注册,你即为账户持有人)。」

也就是说不存在「2~3 个额度」这回事 —— 一个团队的 Apple Distribution 证书只有一个,"新建"的实质就是替换掉团队现有的那一个。Revoke 发布证书会导致依赖它的描述文件全部失效,正在用的千万别动。

与之相对,开发证书属于个人:同一团队里每个人各有自己的开发证书,Apple 会在证书名后追加电脑名以区分(如 Gita Kumar (Work Mac))。

5. 申请认证文件(证书 / App ID / 设备 / 描述文件)

以下操作在 macOS + 浏览器上完成,后台地址:https://developer.apple.com/account/resources/

5.1 第一步:在 Mac 上生成 CSR

CSR(Certificate Signing Request,证书签名请求)是一个包含你公钥和身份信息的文件,Apple 拿它签出证书。

  1. 打开「钥匙串访问」(Keychain Access,在「应用程序 → 实用工具」里,或 Spotlight 搜 Keychain);
  2. 菜单栏 → 钥匙串访问 → 证书助理 → 从证书颁发机构请求证书… (英文:Keychain Access → Certificate Assistant → Request a Certificate From a Certificate Authority…)
  3. 填写:
  4. 用户电子邮件地址:填开发者账号邮箱;
  5. 常用名称(Common Name):起个能认出来的名字,例如 Example iOS Distribution 2026
  6. CA 电子邮件地址:留空;
  7. 请求方式:选 存储到磁盘(Saved to disk),并勾选「让我指定密钥对信息」;
  8. 下一步,密钥大小 2048 位,算法 RSA
  9. 保存得到 CertificateSigningRequest.certSigningRequest 文件。

这一步同时会在你的钥匙串「密钥」分类里生成一对公钥/私钥。私钥别删,删了证书就废了。

5.2 第二步:申请证书(Certificates)

后台 → Certificates → 左上 +

要做的事 选哪个类型 说明
真机调试 Apple 开发(Apple Development) 装到自己登记的设备上跑
上架 / TestFlight Apple 分发(Apple Distribution) 发布包用。后台里的「iOS 开发 / iOS 分发」已被官方标注为「适用于 Xcode 11 及更早版本」,新工程一律选「Apple 开发 / Apple 分发」
推送(壳里暂未集成) Apple Push Notification service SSL 需要时再申请

步骤:

  1. 选类型 → Continue;
  2. 上传刚才的 .certSigningRequest 文件 → Continue;
  3. Download 得到 .cer 文件(如 distribution.cer);
  4. 双击 .cer 导入钥匙串。导入后在钥匙串「我的证书」里能看到证书下面挂着一把私钥(可以展开三角箭头看到)——能展开看到私钥,才算真的可用

常见错误:钥匙串里证书显示「此证书是由未知颁发机构签名的」。原因是缺少 Apple 的中间证书(Apple Worldwide Developer Relations Certification Authority)。到 https://www.apple.com/certificateauthority/ 下载对应的 WWDR 中间证书双击安装即可。2021-01-28 之后签发的证书走的是新的 WWDR 媒介证书,该媒介证书于 2030-02-20 到期;更早签发的证书仍依赖旧媒介证书,两者可以并存,别把旧的删掉。

5.3 第三步:注册 App ID(Identifiers)

后台 → Identifiers → + → 选 App IDs → 选 App

  • Description:填应用描述,例如 QiYuXingHe Mobile(这里不能填中文);
  • Bundle ID:选 Explicit(显式),填 com.d3.app ——必须和工程里 PRODUCT_BUNDLE_IDENTIFIER 完全一致,也要和 capacitor.config.tsappId 一致;
  • Capabilities:勾选这个 App 需要的能力。壳里集成的插件(相机、文件、分享、状态栏、键盘、震动、启动图、热更新)都不需要额外勾特殊 Capability,保持默认即可;如果以后加推送、Sign in with Apple、后台模式,要回来勾上并重新生成描述文件。

Bundle ID 一旦注册就不能删除、不能改(只能停用),所以别拿正式包名做测试。要试手就用 com.d3.app.test 之类。

仓库默认值(见 ios/App/App.xcodeproj/project.pbxproj):PRODUCT_BUNDLE_IDENTIFIER = com.d3.app,换成你自己的 Bundle ID。

5.4 第四步:登记测试设备(Devices)

只有 Development 和 Ad Hoc 需要;上架包(App Store 类型)不需要。

  1. 拿 UDID:iPhone 连 Mac → 打开 Finder(或 Xcode → Window → Devices and Simulators)→ 点设备名可以切换显示 UDID → 右键复制;
  2. 后台 → Devices → + → Platform 选 iOS → 填 Device Name 和 UDID → Continue → Register。

🔴 设备数量规则:Apple 官方原文为「在每个会员资格年度,对于每个产品系列,Apple Developer Program 和 Apple Developer Enterprise Program 会员可以注册最多 100 台以下设备:Apple TV / Apple Vision Pro / Apple Watch / iPad / iPhone / iPod touch / Mac」。要点:

  • 是「每个产品系列各 100 台」,不是所有设备合计 100 台;
  • 停用(Disable)设备并不会增加可用设备数量
  • 新的会员资格年度开始后,具有「账户持有人 / 管理 / App 管理」职能的人首次登录「证书、标识符和描述文件」时,可以移除设备并把每个产品系列的计数复位到 100。这是一年一次的机会,当次不做就得等下个年度;
  • 会员资格到期前 30 天可以下载设备列表,或选择在到期时立即移除设备;若不做任何选择,到期日 180 天后 Apple 会自动移除全部设备

其他细节:注册设备需要「账户持有人或管理」职能;支持批量导入 Apple Configurator 2 导出的 .deviceids 文件或制表符分隔的 .txtApple 芯片的 Mac 需要「预置 UDID(Provisioning UDID)」,不是普通 UDID。

5.5 第五步:创建描述文件(Profiles)

后台 → Profiles → +,按用途选类型:

类型 用途 需要什么
iOS App Development Xcode 连真机调试 Development 证书 + App ID + Devices
Ad Hoc 打包 .ipa 发给指定设备装(不走商店) Distribution 证书 + App ID + Devices
App Store Connect 上架 / TestFlight Distribution 证书 + App ID(不选设备

步骤(以 App Store 类型为例):

  1. App Store Connect → Continue;
  2. 选 App ID:com.d3.app → Continue;
  3. 选证书:选刚申请的 Apple Distribution → Continue;
  4. Provisioning Profile Name,建议带用途和日期,例如 Example AppStore 20260903
  5. Generate → Download 得到 .mobileprovision 文件;
  6. 双击导入(Xcode 会自动放到 ~/Library/MobileDevice/Provisioning Profiles/)。

描述文件有效期:Apple 帮助文档未明写具体天数(通常跟随证书有效期,约 1 年,以后台显示的到期时间为准)。过期后 App 装不上、也传不上去,需要重新生成。日历上记一下到期时间,别等发版时才发现。

🔴 重要机制(针对 2021-06-06 之后创建的团队):用开发或 Ad Hoc 描述文件签名的 App,首次启动时需要联网通过 Apple 的 PPQ 验证,因此本地网络与防火墙必须放通 https://ppq.apple.com。Apple 为离线场景提供有效期 7 天的离线预置描述文件;若设备需要离线超过 30 天,得单独向 Apple 申请。另外所有预置描述文件现在都包含 DER 编码版本,iOS 15 及以上的部分 entitlement 可能要求使用 DER 版本。

5.6 第六步(可选):自动签名 vs 手动签名

仓库里默认走自动签名CODE_SIGN_STYLE = Automatic)。

自动签名(Automatically manage signing) 手动签名(Manual)
谁管证书和描述文件 Xcode 自动申请、下载、续期 你自己在后台建,手动选
优点 省事,个人和小团队首选;壳里已配好,直接用 可控,CI/CD 和多人协作可复现
缺点 Xcode 会自作主张新建证书;由于每个团队每种类型只有一个分发证书,这等于直接顶掉团队现有的分发证书,多人协作时互相踩 步骤繁琐,过期要手动换
要填什么 只要选 Team 要选 Provisioning Profile + Signing Certificate

建议:本地开发用自动签名;如果以后接 CI(比如 GitHub Actions 打 iOS 包),必须改成手动签名,并把 .p12 + .mobileprovision 作为加密 Secret 注入。

5.7 认证文件的导出与保管

要换电脑或者给团队里其他人用,不能只给 .cer,必须导出带私钥的 .p12

  1. 钥匙串访问 → 「我的证书」找到目标证书;
  2. 右键 → 导出
  3. 格式选 个人信息交换(.p12)
  4. 设置导出密码(这个密码要单独记,导入时要用);
  5. 得到 xxx.p12

保管要求(和 Android 的 release.jks 同等重要):

  • .p12 + 导出密码 + .mobileprovision 三样,存到公司密码管理器 / 加密盘,至少两处异地备份
  • 绝对不能提交到 Git 仓库(仓库公开时尤其注意);
  • 谁能拿到要有记录,人员离职要评估是否吊销重签;
  • Apple 账号的双重认证设备/手机号也要有备份人。

丢失 .p12 的后果比 Android 丢 .jks 轻一些——Apple 允许你 Revoke 旧证书重新申请,App 不会因此无法更新。但 Revoke 会让所有依赖它的描述文件失效,正在灰度/审核的版本会受影响。

6. 把签名配置接入工程

6.1 Xcode 图形界面(当前项目走这条)

打开 ios/App/App.xcworkspace(工程用了 CocoaPods,不要双击 .xcodeproj)后:

  1. 左侧选中 App 这个 Target;
  2. 打开 Signing & Capabilities 标签页;
  3. 勾选 Automatically manage signing
  4. Team 下拉选择你的公司团队;
  5. 确认 Bundle Identifiercom.d3.app
  6. 如果报红(比如「No profiles for 'com.d3.app' were found」),点旁边的 Try Again,或检查 5.3 的 App ID 是否注册过。

配置签名

6.2 工程里签名相关的实际配置项

ios/App/App.xcodeproj/project.pbxproj 里与签名、版本相关的关键构建设置:

配置项 仓库默认值 说明
PRODUCT_BUNDLE_IDENTIFIER com.d3.app 必须与 App ID、capacitor.config.tsappId 一致
CODE_SIGN_STYLE Automatic 自动签名
DEVELOPMENT_TEAM 第一次在 Xcode 里打开工程时选自己的 Team,Xcode 会把 10 位 Team ID 写进来
CODE_SIGN_IDENTITY iPhone Developer 项目级默认值
IPHONEOS_DEPLOYMENT_TARGET 14.0 最低支持 iOS 14
MARKETING_VERSION 1.0 对应 CFBundleShortVersionString,即用户看到的版本号
CURRENT_PROJECT_VERSION 1 对应 CFBundleVersion,构建号
SWIFT_VERSION 5.0
TARGETED_DEVICE_FAMILY 1,2 1=iPhone,2=iPad,即支持双端

上架版本号规则:每次上传到 App Store Connect,CURRENT_PROJECT_VERSION(构建号)必须比上一次大,否则会被拒收(提示 build already exists)。MARKETING_VERSION 是给用户看的,同一个 marketing version 下可以有多个构建号。

6.3 Info.plist 里必须写清楚的权限说明

这是 iOS 审核最高频的驳回点:用了某个隐私能力但没写用途说明,或者说明写得敷衍(比如只写「用于相机」),会被以「隐私说明不充分」驳回。

壳里 ios/App/App/Info.plist 已配置的隐私说明:

Key 仓库默认文案 对应能力
NSCameraUsageDescription 用于扫码配置服务器 相机(扫码)
NSPhotoLibraryUsageDescription 用于从相册选择二维码图片配置服务器 相册读取

其他相关配置:

Key 作用
CFBundleDisplayName Example App 桌面显示名。注意:这个值和 capacitor.config.tsappName示例应用)不一致,两处都是占位值,上架前统一改成自己的
UIFileSharingEnabled true 允许「文件」App 访问沙盒 Documents——这是能在「文件 → 我的 iPhone → 应用显示名」里取文件的原因
LSSupportsOpeningDocumentsInPlace true 同上,配合文件浏览
UILaunchStoryboardName LaunchScreen 启动页 storyboard
UIStatusBarHidden / UIStatusBarStyle true / LightContent 状态栏,配合 @capacitor/status-bar
UISupportedInterfaceOrientations 竖屏 + 左右横屏 注意与 Android 的 screenOrientation="portrait"(锁竖屏)不一致
UIRequiredDeviceCapabilities armv7 Capacitor 默认值
NSAppTransportSecurityNSExceptionDomains 给示例域名 your-server.example.com 开了 HTTP 例外(NSTemporaryExceptionAllowsInsecureHTTPLoads = true,最低 TLS 1.2) 见下方警告

⚠️ ATS 例外是审核风险点NSTemporaryExceptionAllowsInsecureHTTPLoads 表示允许对该域名走非 HTTPS。Apple 审核可能会要求你说明理由,或直接驳回。上架前的正确做法是让服务端上 HTTPS,然后把这段例外删掉

如果以后加了定位、麦克风、通讯录等能力,要同步补 NSLocationWhenInUseUsageDescriptionNSMicrophoneUsageDescription 等,文案要具体说明「拿来干什么」

6.4 隐私清单 PrivacyInfo.xcprivacy

🔴 这是这个壳目前最硬的一个上架卡点。 Apple 官方《需要隐私清单和签名的 SDK》名单中明确包含 Capacitor(同一份名单里还有 CordovaSDWebImage)。官方规则是:

  • 提交包含名单内 SDK 的新 App,或提交新增了名单内 SDK 的更新时,必须包含隐私清单
  • 名单内 SDK 作为二进制依赖使用时,还必须带签名

对应到这个壳:

  • 壳用的是 Capacitor 7,正好落在这份名单内;
  • 仓库里 ios/App/App/ 目录下没有 PrivacyInfo.xcprivacy
  • 因此首次提交 App Store 会因缺少隐私清单被卡。这不是"可能收到警告邮件"级别的问题,而是名单内 SDK 的强制要求;
  • 此外壳里集成了 @capacitor/filesystem(访问文件时间戳/磁盘空间)、@capgo/capacitor-updater(读写文件),落在必要理由 API(Required Reason API)范围内,隐私清单里要逐项填 API 类型和理由码;
  • 处理办法:Xcode 里 New File → App Privacy 创建 PrivacyInfo.xcprivacy,按实际情况填写 NSPrivacyAccessedAPITypesNSPrivacyCollectedDataTypes;同时确认 Capacitor 及各插件的 pod 自身是否已带隐私清单与签名,没带的要升到带隐私清单的版本。

7. App Store Connect 创建应用

地址:https://appstoreconnect.apple.com/

7.1 新建 App

「我的 App」→ + → 新建 App:

字段 填法
平台 iOS
名称 App Store 上显示的名字(全局唯一,被占用就得换)。最长 30 字符
主要语言 简体中文
套装 ID(Bundle ID) 下拉选 com.d3.app——只有在 Developer 后台注册过 App ID 才会出现在这个下拉里
SKU 内部编号,用户看不到,自己定一个唯一串(如 example-ios-001
用户访问权限 完全访问权限

7.2 版本信息(必填清单)

说明
截图 按机型尺寸上传。至少要 6.7 英寸 iPhone 一组;有 iPad 支持(仓库默认 TARGETED_DEVICE_FAMILY = 1,2)则还要提供 iPad 截图,不然无法提交
描述 / 关键词 / 推广文本 商店文案
支持网址 / 营销网址 支持网址必填,要能打开
版本号 MARKETING_VERSION 对应
版权 2026 <公司全称>
分级问卷 内容分级,逐项回答
App 隐私 「数据收集」问卷:逐项声明是否收集(联系信息、标识符、使用数据…)。必须和 App 实际行为一致,虚假声明是重点打击对象
隐私政策 URL 必填,必须可访问
登录信息 勾「需要登录」→ 填演示账号密码。产物是登录态应用时不填这个必被拒
备注(App Review Information) 给审核员的补充说明,例如「本 App 需要连接企业私有服务器,服务器地址已在演示账号中预置」
出口合规(Export Compliance) 问你是否使用加密。用了 HTTPS 一般可走「仅使用 Apple 提供/豁免的加密」,需在 Info.plist 里加 ITSAppUsesNonExemptEncryption = false 来免除每次询问(具体豁免口径以 Apple 的表述为准)
广告标识符(IDFA) 问是否使用 IDFA。壳里没接广告 SDK,产物也没接就选「否」

7.3 中国大陆区上架的额外要求

在中国大陆区上架,需要额外准备:

  • App 备案号(工信部备案)。通用规则(来源:华为官方《APP 备案指引》,工信部要求对各分发渠道一致):备案不在应用商店办,在云服务商(接入商)的备案系统办 —— 华为云/阿里云/腾讯云/移动云/天翼云/联通云;存在多个包名时所有包名均需备案;备案的包名、应用名称、主体信息必须与在架信息一致同一主体下不同 App 名称不可重复,同一款 App 在不同运行平台下名称应保持一致。仓库默认 iOS 与 Android 同为 com.d3.app、鸿蒙为 com.d3.app_qyxh两个包名都要备案;若安卓/iOS 已备案而后补鸿蒙,需申请变更备案
  • 无需备案的两类:单机应用(未通过公共互联网提供互联网信息服务)、境外应用(境外主体运营且服务器仅置于境外)。
  • 实操坑:填「主体证件号」时要分清数字 5 与字母 S、数字 1 与字母 I、数字 0 与字母 O,这是最常见的填错项。
  • 部分品类需要软件著作权证书或行业许可证:游戏需版号;金融/医疗/新闻/影视/直播/招聘/地图等需对应资质;深度合成或生成式 AI 还需互联网信息服务算法备案https://beian.cac.gov.cn/)、《安全评估报告》以及显式标识截图或《人工智能生成合成内容标识说明函》。

备案是国内独有环节,耗时可能比 Apple 审核长得多,要提前启动。

上述备案规则以华为官方《APP 备案指引》为参考(工信部要求对各分发渠道一致),实际以你所用云服务商备案系统的提示为准。

8. 打包上传(Archive → Distribute)

8.1 Xcode 图形界面方式

  1. 先按第二部分把 Web 产物同步进 iOS 工程(产物丢进 h5_iOS_Android/distnpx cap copy ios);
  2. Xcode 顶部设备选择器选 Any iOS Device (arm64)——必须是这个,选模拟器时 Archive 是灰的
  3. 菜单 Product → Archive
  4. 构建完成后自动弹出 Organizer 窗口(也可以 Window → Organizer 打开);
  5. 选中刚生成的 Archive → 点 Distribute App
  6. 选分发方式:
  7. App Store Connect → 上架/TestFlight(最常用);
  8. Ad Hoc → 导出 .ipa 给登记过的设备安装;
  9. Development → 内部调试导出;
  10. Enterprise → 仅企业开发者账号;
  11. Upload(直接传)或 Export(先导出 ipa,再用 Transporter 传);
  12. 走完签名确认 → Upload → 等进度条。

8.2 命令行方式(CI 用)

# 1. 归档
xcodebuild -workspace ios/App/App.xcworkspace \
  -scheme App \
  -configuration Release \
  -archivePath build/App.xcarchive \
  archive

# 2. 导出 ipa(需要一个 ExportOptions.plist)
xcodebuild -exportArchive \
  -archivePath build/App.xcarchive \
  -exportPath build/ipa \
  -exportOptionsPlist ExportOptions.plist

# 3. 上传(二选一)
xcrun altool --upload-app -f build/ipa/App.ipa -t ios \
  -u <AppleID> -p <应用专用密码>
# 或新版工具
xcrun notarytool / Transporter

注意用 .xcworkspace 而不是 .xcodeproj——壳工程用了 CocoaPods,必须走 workspace,否则找不到 Pods。

ExportOptions.plist 最小示例(App Store 分发):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>app-store</string>
    <key>teamID</key>
    <string>你的 Team ID</string>
    <key>uploadSymbols</key>
    <true/>
</dict>
</plist>

altool--upload-app 参数在新版 Xcode 里已提示弃用,改用应用专用密码(App-Specific Password)或 API Key(--apiKey / --apiIssuer)。以你本机 Xcode 的实际提示为准。

8.3 TestFlight 内部测试

上传成功后(处理通常要几分钟到几十分钟),构建版本会出现在 TestFlight 里:

  • 内部测试上限 100 名内部测试员,成员需具备「账户持有人 / 管理 / App 管理 / 开发者 / 营销」职能,不需要 Apple 审核,可以立刻装;
  • 外部测试最多 10,000 名外部测试员;🔴 第一个构建版本必须先获得 App Review 针对 TestFlight 的批准才能发给外部测试员;
  • 其他配额:一个 App 最多可分享 100 个构建版本给测试员,每个测试员最多可在 30 台设备上安装;

强烈建议先走一轮内部 TestFlight,确认「装上能起、能连服务器、能登录」再提交正式审核。iOS 审核被拒一次要重排队。

9. 提交审核与常见驳回原因

9.1 提交

版本信息填完 + 选好构建版本 → 右上角 添加以供审核 / 提交以供审核

审核状态流转:

准备提交 → 正在等待审核 → 正在审核 → 待发布 / 已拒绝
Apple 官方文档没有承诺固定审核时长。经验值 24~48 小时,节假日和大版本期(如 iOS 新版本发布前)会更慢。

发布方式可选: - 自动发布:审核通过立即上架; - 手动发布:通过后停在「待发布」,你点按钮才上; - 定时发布:指定日期。

建议选手动发布,给自己留一个「审核通过但先别上」的缓冲。

9.2 常见驳回原因(对着这个壳的情况看)

驳回原因 与这个壳的关系 处理
需要登录但没给演示账号 产物需要登录才能进主界面时高危 App Review Information 里填可用的账号密码,并确认审核期间账号有效
需要连接私有服务器,审核员连不上 产物连的是自有服务器或内网服务器时 备注里说明清楚;确保服务器公网可达、演示账号能登;必要时提供服务器配置二维码截图
隐私说明不充分 已配相机/相册说明,但文案偏简短 NSCameraUsageDescription 等文案写具体(「用于扫描二维码以配置企业服务器地址」)
App 隐私问卷与实际不符 产物往 localStorage 存账号、登录时间等信息时 在「App 隐私」里如实声明标识符/联系信息的收集
明文 HTTP 传输 Info.plist 有 ATS 例外 上 HTTPS 并删除例外,见 6.3
动态下发代码(热更新) 壳里带了 @capgo/capacitor-updater 热更新能力 重点:Apple 允许更新 JS/资源,但不允许改变 App 主要功能或绕过审核。要保证热更新只更新已审核功能的 Web 资源,不新增未审核功能;不要在审核期间下发差异内容
仅是网站的封装(4.2 最低功能性) 这就是个 WebView 壳,高危 强调调用了原生能力(相机扫码、文件读写、分享、震动、状态栏、启动页),并在描述中体现原生价值
iPad 上无法正常使用 TARGETED_DEVICE_FAMILY = 1,2 声明支持 iPad 要么真的适配 iPad 并提供截图,要么改成只支持 iPhone(1 省事
崩溃 / 白屏 Web 资源没同步进包时会白屏 Archive 前一定确认 npx cap sync ios 已执行、ios/App/App/public 里有资源(见 13 节)
元数据问题 截图与实际界面不符、含其他平台字样(如出现「Android」「安卓」) 检查截图和描述文案

9.3 被拒之后

  • 在 App Store Connect 的「解决方案中心」看 Apple 的具体理由(会附截图/日志);
  • 纯元数据问题(截图、描述、隐私问卷)改完直接重新提交,不需要重新打包;
  • 代码问题要改代码 → 提高 CURRENT_PROJECT_VERSION → 重新 Archive 上传;
  • 认为判断有误可以在解决方案中心申辩,或提交 App Review Board 申诉。

10. 上架后的版本更新

  1. 改代码 → 提升版本号:
  2. 功能更新:MARKETING_VERSION1.01.1
  3. 构建号 CURRENT_PROJECT_VERSION 必须递增
  4. 重新把产物丢进 distnpx cap copy ios → Archive → 上传;
  5. App Store Connect「我的 App」→ 点 + 版本 创建新版本 → 填「此版本的新增内容」→ 选新构建版本 → 提交审核;
  6. 也可以用分阶段发布(Phased Release),让新版本按天逐步推给用户,出问题可以暂停。

关于走热更新而不是走商店更新

壳里集成了 @capgo/capacitor-updatercapacitor.config.tsautoUpdate: falsestatsUrl: '')。

  • 能做:修 Web 层的 bug、调样式、改文案,不用过审;
  • 不能做:新增未审核的功能模块、改变 App 用途、绕过 Apple 审核机制。这是 App Store 审核指南明确禁止的(对应 2.5.2 / 3.1.1 等条目),违规风险是直接下架
  • 原生层的改动(新增插件、改 Info.plist、改权限)必须走商店更新,热更新覆盖不到。

第二部分 构建与打包操作文档

本部分对应 h5_iOS_Android 的纯壳工程结构。 iOS 的构建和上传只能在 macOS + Xcode 上做,Windows / Linux 只能改代码,出不了包。

11. 环境要求

要求 备注
操作系统 macOS Windows / Linux 无法编译 iOS,硬性限制
Node / npm Node ≥ 20 只用来跑 Capacitor CLI
Xcode 支持 iOS 14+ 的版本 从 App Store 或 Apple Developer 下载
CocoaPods 1.6+ Capacitor 插件靠它引入
真机 iOS 14 以上设备 工程 IPHONEOS_DEPLOYMENT_TARGET = 14.0;需要能传数据的数据线
开发者账号 Apple Developer Xcode 登录的 Apple ID 要和开发者账号一致

不需要装任何前端构建工具。这个工程只是原生壳,Web 产物在别处构建好后丢进 dist/

工程结构

h5_iOS_Android 是只含原生壳的 Capacitor 工程,里面没有业务代码:

路径 说明
h5_iOS_Android/dist/ 放 Web 产物的地方webDir 指向它,根目录必须有 index.html
h5_iOS_Android/ios/ iOS 壳工程
h5_iOS_Android/ios/App/App.xcodeproj Xcode 工程文件
h5_iOS_Android/ios/App/App.xcworkspace 接了 CocoaPods 后用这个打开
h5_iOS_Android/ios/App/Podfile CocoaPods 依赖清单
h5_iOS_Android/ios/App/App/Info.plist iOS 配置与权限说明
h5_iOS_Android/ios/App/App/AppDelegate.swift 应用入口,无定制改动
h5_iOS_Android/ios/App/App/Assets.xcassets/ 图标(AppIcon.appiconset)与启动图(Splash.imageset
h5_iOS_Android/ios/App/App/Base.lproj/ LaunchScreen.storyboard / Main.storyboard
h5_iOS_Android/ios/App/App/public/ 同步进来的 Web 产物,由 cap copy 生成,被 .gitignore 忽略
h5_iOS_Android/android/ Android 壳工程
h5_iOS_Android/capacitor.config.ts Capacitor 配置:appIdappNamewebDir、各插件配置
h5_iOS_Android/resources/ 图标与启动图源文件(logo.png / splash.png
h5_iOS_Android/package.json 只有 Capacitor CLI 与插件依赖

壳里集成的 Capacitor 插件(iOS 侧,见 ios/App/Podfile

Pod npm 包 用途
Capacitor @capacitor/core + @capacitor/ios 核心运行时
CapacitorCordova 同上 Cordova 插件兼容层
CapacitorApp @capacitor/app 应用生命周期、硬件返回键
CapacitorDialog @capacitor/dialog 原生弹窗
CapacitorFilesystem @capacitor/filesystem 文件读写
CapacitorHaptics @capacitor/haptics 震动
CapacitorKeyboard @capacitor/keyboard 键盘
CapacitorShare @capacitor/share 分享
CapacitorSplashScreen @capacitor/splash-screen 启动画面
CapacitorStatusBar @capacitor/status-bar 状态栏
CapgoCapacitorUpdater @capgo/capacitor-updater 热更新,可从远端拉 dist.zip 切换版本

Podfile 关键设置:platform :ios, '14.0'use_frameworks!install! 'cocoapods', :disable_input_output_paths => true(避免 Xcode 缓存 Pods 的问题)、post_install 里调 assertDeploymentTarget

capacitor.config.ts 里的 iOS 相关配置

配置 说明
appId com.d3.app 必须与 Bundle ID 一致
appName 示例应用 占位值
webDir dist Web 产物目录
ios.scheme 示例应用 中文占位值,建议改成英文
ios.contentInset automatic 内容自动避让安全区
ios.appendUserAgent Iosappmessenger 服务端可据此识别 iOS 客户端
ios.backgroundColor #ffffff
ios.zoomEnabled true 允许缩放
ios.limitsNavigationsToAppBoundDomains false
插件 SplashScreenlaunchShowDuration: 3000launchAutoHide: false、全屏沉浸)、StatusBarstyle: DARKoverlaysWebView: true)、Keyboardresize: none)、CapacitorUpdaterautoUpdate: false)、CapacitorAssetsresources/logo.pngresources/splash.png launchAutoHide: false 意味着Web 产物必须自己调 SplashScreen.hide(),见第三部分第 5 条

12. 开发工具

  1. Xcode:编译、签名、Archive、真机调试都在这里,打开 ios/App/App.xcworkspace
  2. CocoaPods:装 Capacitor 插件的原生依赖,在 ios/App 目录下执行 pod install
  3. 终端:在 h5_iOS_Android 根目录执行 npm installnpx cap copy iosnpx cap sync ios
  4. 真机:数据线连 Mac,Xcode 能识别后即可调试。

壳工程不需要任何前端框架工具链,Web 产物是在别的项目里构建好后丢进 dist/ 的。

13. 构建步骤

h5_iOS_Android 是纯原生壳,本身不构建 Web 代码。流程是:在别的项目里把 Web 产物打好包 → 丢进 h5_iOS_Android/dist/ → 用 Capacitor 同步进 iOS 工程 → 在 Xcode 里编译。

13.1 首次拿到代码(只做一次)

cd h5_iOS_Android
npm install                  # 装 Capacitor CLI 与各插件
npx cap sync ios             # 生成原生依赖配置
cd ios/App && pod install    # 装 Pods

三步都不能跳:

  • ios/App/Podfile 里每个 pod 都写成 ../../node_modules/@capacitor/* 相对路径,没有 node_modulespod install 失败;
  • cap sync 负责写 ios/App/App/capacitor.config.json、生成 capacitor-cordova-ios-plugins/,这些都在 .gitignore 里,克隆下来没有;
  • Pods/ 目录由 pod install 生成,仓库里不含(App.xcworkspace 本身已提交,但没有 Pods 打不开)。

13.2 日常:换一次 Web 产物

# 1. 把产物(index.html + 静态资源)放进 h5_iOS_Android/dist
# 2. 拷进 iOS 工程
npx cap copy ios
# 3. 打开 Xcode
npx cap open ios

dist/ 根目录下必须有 index.html,这是 WebView 的入口。

package.json 里已经配好的 scripts:

命令 等价于 什么时候用
npm run copy cap copy 只换了 dist 内容(日常最常用)
npm run sync cap sync 改了 capacitor.config.ts 或增删了插件
npm run open:ios cap open ios 打开 Xcode
npm run assets capacitor-assets generate 换了图标 / 启动图

cap open ios 打开的是 App.xcworkspace。手动打开时也要选 .xcworkspace不要双击 App.xcodeproj,否则找不到 Pods。

13.3 产物落在哪

npx cap copy iosh5_iOS_Android/dist/ 整个拷到 ios/App/App/public/。这个目录被 ios/.gitignore 忽略,所以:

  • 克隆下来它是空的,正常;
  • Xcode 编译时打进包的是 public/,不是 dist/
  • 只改 dist 不执行 copy,打出来的包内容不会变。

不要执行 npx cap add ios 仓库里的 ios 目录是已经配好的(签名设置、Info.plist 权限说明、启动图资源都在里面),add 会重新生成、覆盖掉这些改动。

13.4 需要自己确认的工程配置

位置 仓库默认值 说明
capacitor.config.tsappId com.d3.app 必须与 App ID、PRODUCT_BUNDLE_IDENTIFIER 一致
capacitor.config.tsappName 示例应用
capacitor.config.tsios.scheme 示例应用 中文占位值,建议改成英文
project.pbxprojPRODUCT_BUNDLE_IDENTIFIER com.d3.app 在 Xcode 的 Signing & Capabilities 里改
project.pbxprojDEVELOPMENT_TEAM 第一次打开必须自己选 Team
Info.plistCFBundleDisplayName Example App 桌面图标下显示的名字
Info.plist → 权限说明 相机 + 相册 按实际用到的能力删减
Info.plistNSAppTransportSecurity your-server.example.com 占位域名,按自己的接口域名改或整段删

14. 打包调试

把构建成功的项目在 Xcode 中操作:

第 1 步:在项目的「Signing & Capabilities」标签页配置签名(详见第 6 节)

配置签名

第 2 步:用可以传输数据的数据线将真机连接到 Mac 后,Xcode 可以识别到手机,然后就可以真机调试

调试运行操作

第 3 步:在构建成安装包后,要在手机「设置」中打开开发者模式 (路径一般是:设置 → 隐私与安全性 → 开发者模式 → 打开 → 重启手机)

第 4 步:在手机设置中找到 APP,对手机弹出的提示点击信任构建后的 APP

第 2 步操作后会遇到:

第2步操作后会遇到

根据提示进行操作:

根据提示进行操作

点击信任:

点击信任

手机上的路径一般是:设置 → 通用 → VPN与设备管理 → 开发者 App → 点开你的证书 → 信任。

第 5 步:信任后再去 Xcode 执行一次安装就可以了。

15. 应用图标与启动页

图标和启动图由 @capacitor/assets 生成,工程已经把它放在 devDependencies 里,不用再单独安装。

源文件放在 h5_iOS_Android/resources/

文件 用途 建议规格
resources/logo.png 应用图标源图 1024×1024 PNG
resources/splash.png 启动图源图 2732×2732 PNG,主体居中

替换这两张图后执行:

npm run assets       # 等价于 npx capacitor-assets generate
npx cap copy ios

生成结果写进 ios/App/App/Assets.xcassets/AppIcon.appiconset/(图标)和 Splash.imageset/(启动图,含 1x/2x/3x 三张 2732×2732)。

  • 源图目录名只能是 resourcesassets,改成别的名字工具找不到;
  • 具体路径由 capacitor.config.tsCapacitorAssets.resources 指定,已经指向上面两个文件;
  • 启动图的实际布局走 Info.plistUILaunchStoryboardName = LaunchScreen,对应 ios/App/App/Base.lproj/LaunchScreen.storyboard。生成器改不到布局时,直接在 Xcode 里打开这个 storyboard 换图,替换红框内的图片即可:

启动页贴图配置

上架用的 App Store 图标是 1024×1024 PNG,不能带 Alpha 通道、不能自己做圆角,否则上传时会被 ITMS 报错拒收。

16. 注意事项

  1. 必须安装 CocoaPods,Capacitor 插件的原生依赖全靠它引入,版本要 1.6+。
  2. 每次换 Web 产物都要 npx cap copy ios,只改 dist 不同步,包里还是旧内容。
  3. 改了 capacitor.config.ts 或增删了 Capacitor 插件,要先 npx cap sync iospod install,光 copy 不会更新原生依赖。
  4. Info.plist 的权限说明按产物实际用到的能力删减。 壳里声明了相机(NSCameraUsageDescription)和相册(NSPhotoLibraryUsageDescription),并开了 UIFileSharingEnabled + LSSupportsOpeningDocumentsInPlace(让「文件」App 里能看到应用目录)。用不到就删。
  5. NSAppTransportSecurity 里的 your-server.example.com 是占位域名,换成自己的接口域名,接口全走 HTTPS 时可以整段删掉。
  6. iOS 上取文件/日志的位置:文件 App → 我的 iPhone → 应用显示名(仓库默认是 Example App,来自 Info.plistCFBundleDisplayName),能看到是因为开了 UIFileSharingEnabled
  7. IPHONEOS_DEPLOYMENT_TARGET = 14.0,低于 iOS 14 的设备装不上。
  8. 滑动返回等手势在 iOS WebView 里的行为和 Android 不一致,属于平台差异,不是 bug。

第三部分 常见坑

  1. 漏了 npm installnpx cap sync ios 就直接开 Xcode,必定编不过。 Podfile 里每个 pod 都指向 ../../node_modules/@capacitor/*Pods/capacitor-cordova-ios-plugins/ 都是同步阶段生成的,仓库里没有。
  2. 只把产物丢进 dist 却没执行 npx cap copy ios,打出来的还是上一次的内容。 dist 不参与 Xcode 编译,真正被打进包的是 ios/App/App/public/
  3. Xcode 必须用 App.xcworkspace 打开(工程用了 CocoaPods),双击 App.xcodeproj 会找不到 Pods。
  4. ios/App/App/public/ios/.gitignore 忽略,克隆下来是空的,属于正常现象,cap copy 之后才有内容。
  5. 启动图不会自己消失。 capacitor.config.tsSplashScreen.launchAutoHide: false,原生侧没有兜底隐藏逻辑,Web 产物必须自己调 SplashScreen.hide(),否则一直停在启动图上。
  6. 产物不要依赖外部 CDN。 App 里是本地环境,产物如果留了指向外网的 importmap、external 依赖或 <script src="https://...">,域名不通时直接白屏,且没有任何报错界面。产物要自包含。
  7. 仓库里 DEVELOPMENT_TEAM 是空值CODE_SIGN_STYLE = Automatic),第一次在 Xcode 打开必须自己选 Team,否则报 Signing for "App" requires a development team
  8. Xcode 里登录的 Apple ID 必须和开发者账号是同一个,否则拉不到证书和描述文件。
  9. CURRENT_PROJECT_VERSION(构建号)每次上传必须递增,否则被拒收(提示 build already exists)。仓库里是 1
  10. 仓库里没有 PrivacyInfo.xcprivacy 隐私清单:Capacitor 属 Apple 强制隐私清单 SDK 名单,首次提交会被卡,见 6.4。
  11. pod install 失败先看 CocoaPods 版本Podfile 用了 install! 'cocoapods', :disable_input_output_paths => true,需要 CocoaPods 1.6+。
  12. 仓库里的标识都是示例值appId / PRODUCT_BUNDLE_IDENTIFIERcom.d3.appappNameios.scheme 是中文 示例应用CFBundleDisplayNameExample AppInfo.plist 里还留了 your-server.example.com 的 HTTP 例外域名。上架前逐项改成自己的。
  13. Info.plist 声明了相机和相册权限,如果你的产物用不到,把 NSCameraUsageDescription / NSPhotoLibraryUsageDescription 删掉,否则审核会追问用途。
搜索