Cap iOS App Store 发布指南:Expo EAS 构建、审核合规与上架全流程
【免费下载链接】CapOpen source Loom alternative. Beautiful, shareable screen recordings.项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap
Cap 移动端当前是一个仅支持 iPhone 的 Expo 应用,本篇指南以仓库内 app-store-release.md 的发布检查清单为核心骨架,结合 app.config.js、eas.json、store.config.json 等工程文件与移动端源码,完整讲解从 EAS 构建、App Store Connect 配置、隐私合规、审核备注到最终验证序列的全套 iOS 上架实操方案。读完你既可以照单执行 Cap 的 1.0 发布流程,也能把同一套检查与验证方法论迁移到自己的 Expo 应用中。
发布范围与前提:iPhone-only 的 Expo 应用
Cap Mobile 是运行在 Expo SDK 55 / React Native 0.83 / React 19 之上的 iPhone-only 应用,工程采用 Expo Router 与 Continuous Native Generation(CNG)模式,原生工程由本地或 EAS 生成、不入库(见 apps/mobile/README.md)。其发布边界明确如下:
- 仅支持 iOS/iPhone:Android 暂不在此次发布范围内,原因是原生录制模块没有 Android 实现,且 Expo 配置(
platforms: ["ios"]、supportsTablet: false)刻意只包含 iOS。 - 纯手机形态:应用不面向 iPad,因此 iPad 截图与平板行为均不在发布范围之内。
- 发布口径:1.0 采用"手动构建再手动提交"(manual build-then-submit)工作流,
store.config.json中release.automaticRelease与phasedRelease均为false,即审核通过后由人工手动发布。
发布阻塞项(Release blockers)
文档以勾选清单的形式记录了发布前必须全部完成的阻塞项,其中已完成与待办事项共同构成了一份可复用的发布就绪检查表:
| 状态 | 事项 |
|---|---|
| ✅ | 创建cap-software-incExpo 组织,并以richiemcilroy为 owner |
| ✅ | 将应用关联到 Cap 拥有的cap-software-inc/cap-mobileEAS 项目,并在 app.config.js 中提交公开的 project ID |
| ⬜ | 确认 Apple Team47B7FCLL43拥有 bundle IDso.cap.mobile,随后在 App Store Connect 创建/选择匹配的应用 |
| ⬜ | 为cap.so配置 Sign in with Apple,设置生产环境APPLE_CLIENT_ID与APPLE_CLIENT_SECRET,并在 TestFlight 上验证完整移动端 OAuth 回调 |
| ✅ | 在审核前移除 iOS 账户页中的外部 Cap Pro 结账动作(1.0 中已有权益仍可用) |
| ✅ | 移除 Pro 门槛的 Analytics 与 Loom Import 页面中的外部购买 CTA |
| ✅ | 允许已登录用户在账户设置中发起永久账户删除 |
| ✅ | 在录制器与已认证的移动端 API 两侧同时执行对外宣传的"五分钟免费录制"限制 |
| ✅ | 支持用户应用内举报 Cap 与拉黑另一 Cap 所有者 |
| ⬜ | 在分发发布构建前先部署移动端 API 变更 |
| ⬜ | 在 App Store Connect 中提供 App Review 联系方式、可长期使用的审核账号与审核说明(审核凭证不得入库) |
| ⬜ | 完成年龄分级、内容权利、DSA(数字服务法)、出口合规与隐私问卷 |
| ⬜ | 使用不含敏感信息的演示内容截取获批准的 6.9 英寸 iPhone 截图 |
| ⬜ | 通过签名生产 EAS 构建、真机 TestFlight 冒烟测试与 App Store Connect 上传校验 |
已验证基础:发布前的质量与合规地基
文档记录了发布前已逐项验证通过的工程基础,这些条目本质上是"上线前自检清单":
- Expo Doctor 全部 19 项检查通过;Expo SDK 55 各包版本对齐;移动端 TypeScript 校验通过。
- 移动端测试套件通过 34 个文件、214 个测试(对应 apps/mobile/package.json 中的
bun run test)。 - 本机安装 Xcode 26.2,满足当前 iOS 26 SDK 上传的最低要求。
- 应用图标为 1024×1024、完全不透明、无 alpha 通道编码。
- 相机、麦克风、Photos 导入、Photos 保存四类权限描述均已配置(见下文
infoPlist与expo-camera插件)。 - 生成的隐私清单中包含 required-reason API 声明。
- iOS 登录面提供 Google、Apple、邮箱验证码与企业 SSO 四种方式;Apple 使用系统原生按钮并开启原生能力,且各提供商按钮仅在其服务器凭据可用时出现。
- 账户设置中向已登录用户开放 Help、Support、Privacy、Terms 页面。
- 内容举报会被持久化存储供支持团队审查,被拉黑所有者的 Caps 与评论会从拉黑用户的移动端结果中移除。
- 移除了未使用的后台处理模式;默认省略 Associated Domains(
cap.so尚无生产关联文件)。 - 出口合规中"非豁免加密"声明为 false,并在 Expo 配置中显式声明发布 Team ID。
- 生产、预览、开发三套 EAS 构建 profile 均已存在,支持远端 build-number 管理与 runtime-version 隔离。
- 干净的 prebuild、CocoaPods 安装、生产 JavaScript 导出、Xcode 26.2 Release 模拟器构建全部通过;构建产物能在 iPhone 17 Pro Max 模拟器上从内嵌生产 bundle 正常启动。
工程配置解读:bundle ID、Team ID 与权限文案
app.config.js:iOS 发布元数据的源头
ios 配置块 是理解整个 iOS 发布约束的钥匙:
const bundleIdentifier = "so.cap.mobile"; const projectId = process.env.EXPO_PROJECT_ID ?? "616ebd7a-e876-4b21-82be-d626028042f6"; const ios = { appleTeamId: "47B7FCLL43", bundleIdentifier, config: { usesNonExemptEncryption: false }, supportsTablet: false, usesAppleSignIn: true, infoPlist: { NSPhotoLibraryUsageDescription: "Cap imports videos from Photos for upload.", NSPhotoLibraryAddUsageDescription: "Cap saves downloaded videos to Photos.", }, };bundleIdentifier: "so.cap.mobile"是最终 bundle ID,Apple Team47B7FCLL43是发布团队,二者是 App Store Connect 记录 创建的基础。usesNonExemptEncryption: false对应 App Store Connect 出口合规问卷中"非豁免加密"声明为 false。usesAppleSignIn: true开启系统级 Sign in with Apple 原生能力;当cap.so具备生产关联文件后,可通过CAP_MOBILE_ASSOCIATED_DOMAINS环境变量注入associatedDomains。supportsTablet: false落实"仅手机"形态,这也解释了为何 iPad 截图不在发布范围。
配置还包含其他与发布强相关的关键项:
- runtime 版本策略:
runtimeVersion.policy为appVersion,即运行时版本与 app 版本绑定;updates.url指向https://u.expo.dev/${projectId},供 EAS Update 使用(project ID 是公开标识,可安全提交)。 - 权限文案集中在插件层:
expo-camera插件声明cameraPermission: "Allow Cap to use your camera to record videos."与microphonePermission: "Allow Cap to use your microphone while recording videos.";expo-secure-store声明faceIDPermission: "Allow Cap to protect your account key.";expo-splash-screen配置了#f9f9f9背景与 splash 图标;expo-font注册了三款 NeueMontreal 字体。 - 原生录制能力:通过
./modules/cap-screen-recorder/app.plugin.js配置appGroup: "group.so.cap.mobile.screen-recording"与extensionBundleIdentifier: "so.cap.mobile.screen-broadcast",配合./modules/cap-recorder与./modules/cap-screen-recorder两个本地原生模块实现相机录制与屏幕广播扩展。
eas.json:三套构建 profile 与提交配置
eas.json 定义了开发、预览、生产三套相互隔离的构建:
| Profile | channel | 关键差异 |
|---|---|---|
development | development | developmentClient: true、distribution: internal、iOS 走模拟器(simulator: true)、开启CAP_MOBILE_BUILD_REACT_NATIVE_FROM_SOURCE=1 |
preview | preview | distribution: internal,面向真机内部测试 |
production | production | credentialsSource: remote、distribution: store、autoIncrement: true(build number 由 EAS 远端自增)、environment: production |
同时cli.appVersionSource为remote,即 app 版本号由 EAS 远端管理,与 README 中"Production build numbers are managed and incremented by EAS"的说法一致。submit.production段则把 App Store Connect 上传所需信息集中声明:appleTeamId: "47B7FCLL43"、bundleIdentifier: "so.cap.mobile"、companyName: "Cap Software, Inc."、language: "en-US"、metadataPath: "./store.config.json"、sku: "cap-mobile-ios"。三个 profile 均固定使用 bun 1.4.0,命令行统一采用bunx eas-cli@21.0.2。
Owner 输入:Expo 与 Apple 侧的人为前置条件
Expo 侧
- 保持应用及其 EAS 订阅都在
cap-software-incExpo 组织下。 - 每次构建、更新与提交都使用已关联的
cap-software-inc/cap-mobileEAS 项目。 - 确认 EAS 计费套餐至少能运行一次生产 iOS 构建。
Apple Developer 与 App Store Connect 侧
- 确认
so.cap.mobile是最终 bundle ID、Apple Team47B7FCLL43是发布团队。 - 确保 Agreements、Tax、Banking 无阻塞协议。
- 给提交账号 App Manager 或 Admin 权限,外加 Certificates, Identifiers & Profiles 访问权限。
- 创建 App Store Connect 记录,并把其数字 Apple ID 作为
ascAppId提供给eas.json。 - 提供 App Review 联系人姓名、邮箱与国际格式电话。
- 创建含安全示例 Caps 的可长期使用审核账号,凭据只存放在 App Store Connect。
- 在 App ID
so.cap.mobile上启用 Sign in with Apple,创建用于 Web OAuth 流程的 Services ID 并关联到主 App ID,注册https://cap.so/api/auth/callback/apple为返回 URL。 - 为 Services ID 生成 Apple client-secret JWT,将 Services ID 作为
APPLE_CLIENT_ID、JWT 作为APPLE_CLIENT_SECRET存入生产 Web 环境,并在过期前轮换。
账户删除与内容安全运营流程
账户删除
- 指定负责人监控
hello@cap.so与人工模式 Messenger 会话中主题为[PENDING] Account deletion request的消息。 - 30 天内完成删除:取消任何直接 Cap 订阅、删除账号及关联的个人数据与内容、处理独家所有组织、移除对共享组织的访问。
- 完成后邮件通知用户,并将持久化请求主题从
[PENDING]改为[COMPLETED]。待处理请求会刻意阻塞所有新的移动端登录。 - 在 App Review 前用一次性账号测试完整流程,但不要删除可长期使用的审核账号。
这一流程在源码中有直接对应:delete-account.test.tsx验证了删除页要求输入确认词DELETE才启用删除按钮、弹出["Request permanent deletion", "Cancel"]的 ActionSheet 二次确认、调用requestAccountDeletion后立即signOut,并在请求失败时保持用户登录(见 apps/mobile/src/screens/delete-account.test.tsx)。这与审核备注中"账户删除位于 Account settings、立即退出登录、阻塞重新登录、并在确认前说明 30 天删除与直接订阅取消流程"的表述一致。
内容安全运营
- 指定负责人监控
hello@cap.so与人工模式 Messenger 会话中主题为[PENDING] Mobile content report的消息。 - 依据 Cap 服务条款及时审查每条举报,删除违规内容,酌情回复举报者,并把主题从
[PENDING]改为[COMPLETED]。 - 保持 App Review 说明准确:原生应用没有公开发现流或匿名聊天,Caps 仅对所有者或有权访问的组织/空间认证成员可见,公共分享链接无法从公开的移动端目录浏览。
- 将组织成员移除与内容移除流程作为服务级审核控制手段;审核前用两个一次性账号测试举报与拉黑行为。
Cap Pro 购买策略:1.0 只展示、不出售
Version 1.0 应展示用户"服务端权威"的 Free 或 Cap Pro 状态,但不提供外部 Stripe 结账——这是最小的合规发布路径,同时为已在其他 Cap 平台订阅的用户保留功能。源码印证了这一设计:getProPlan通过GET /api/desktop/plan拉取{ upgraded, stripeSubscriptionStatus },并由服务端返回的upgraded布尔值驱动 Pro 状态展示(见 apps/mobile/src/billing/pro.ts)。
对于后续版本的原生内购,文档推荐的 Expo 路径是RevenueCat +react-native-purchases:建立proentitlement、月度与年度自动续期订阅产品、restore purchases,以及与 Web/桌面端共用同一 entitlement 的认证后服务端同步。Owner 需要创建 App Store 订阅组与产品、接受 Paid Apps 协议,并通过生产 EAS 环境提供 RevenueCat 公共 iOS API key。任何私有 App Store 或 RevenueCat key 都不属于仓库。
草拟 App Privacy 答案
以下答案必须在发布到 App Store Connect 前,对照生产服务器行为与每个第三方 SDK 复核:
| 数据类型 | 关联到用户 | 追踪 | 用途 |
|---|---|---|---|
| 姓名与邮箱 | 是 | 否 | 账户与应用功能 |
| 用户 ID | 是 | 否 | 认证与应用功能 |
| 照片、视频与音频 | 是 | 否 | 录制、上传、播放与分享 |
| 其他用户内容 | 是 | 否 | 标题、评论、反应与导入内容 |
| 购买历史 | 是 | 否 | 订阅权益与支持 |
| 产品交互 | 是 | 否 | 观看者分析与应用功能 |
当前移动端包不包含任何广告 SDK,也不请求 App Tracking Transparency 权限。
Store Listing 与截图规范
store.config.json 集中存放英文 App Store 名称、副标题、描述、关键词、分类、URL、版权与手动发布策略。EAS Metadata 目前仍是 beta 功能,因此推送后必须逐字段与 App Store Connect 核对。关键字段包括:
- 分类:
["PRODUCTIVITY", "PHOTO_AND_VIDEO"];版权:2026 Cap Software, Inc. - 副标题:
Record, share, collaborate - 关键词:camera recorder、video recorder、async video、video message、teleprompter、video sharing、analytics 等
- 描述要点:iPhone 相机/麦克风录制、从 Photos 或 Files 导入、创建与分享 Cap 链接、浏览个人/组织/空间库、原生播放控制、链接可见性与密码管理、评论/反应/观看分析、Loom 视频导入,以及免费账号最长五分钟录制、Cap Pro 订阅者无限时长等。
- URL 三件套:
marketingUrl: https://cap.so、supportUrl: https://cap.so/docs、privacyPolicyUrl: https://cap.so/privacy(生产环境隐私与条款页面必须返回 HTTP 200)。
推荐截图集(6 张):
- My Caps 库(个人与空间导航)
- 原生相机录制器与提词器(teleprompter)
- 媒体导入与上传进度
- Cap 播放、分享控制与评论
- 观看者分析与反应
- 账户、组织与 Cap Pro 状态
截图须使用 Apple 认可的 6.9 英寸竖屏尺寸之一、去掉所有 alpha 通道,并避免出现真实客户姓名、邮箱、录制内容或分析数据。
App Review 审核备注要点
提交审核时应向评审员说明:
- Cap 需要账号,因为核心体验是私人的个人或组织视频库。
- 邮箱验证码登录与提供的审核账号可以不依赖评审员个人身份提供商而访问所有可审核功能。
- iOS 上 Google 与 Apple 登录在其生产 OAuth 凭据配置后可用;用户也可使用邮箱验证码或企业 SSO。
- iPhone 录制器只捕获相机与麦克风视频,不捕获 iPhone 屏幕。
- Photos 与 Files 访问仅在用户选择导入或保存动作时请求。
- 免费账号可录制最长五分钟。
- 账户删除位于 Account settings,立即退出登录、阻塞重新登录,并在确认前说明 30 天删除与直接订阅取消流程。
- 现有 Cap Pro 权益在 Account settings 中可识别;1.0 不在应用内销售数字功能。
- Cap 是无公开发现流或匿名聊天的认证协作工具;每个 Cap 详情页都包含应用内举报与拉黑,举报进入受监控的支持队列供及时审查。
- 生产 API 与媒体处理服务将在整个审核窗口内保持可用。
最终验证序列:一条可复制的命令行流水线
在配置好 EAS 与 Apple 访问权限后,从apps/mobile目录依次执行(对应 package.json 中的脚本与文档序列):
bunx eas-cli@21.0.2 project:info --non-interactive bunx eas-cli@21.0.2 config --platform ios --profile production bunx expo-doctor@latest bun run typecheck bun run test bun run expo prebuild --platform ios --clean --no-install bunx eas-cli@21.0.2 build --platform ios --profile production每一步的职责:project:info确认 EAS 项目关联;config渲染生产 profile 的最终 iOS 配置(含 bundle ID、Team ID、权限文案);expo-doctor校验 SDK 对齐;typecheck与test把关类型与 214 个测试;prebuild --clean验证 CNG 可重复生成干净原生工程;最后以生产 profile 触发签名构建(生产 build number 由 EAS 远端自增)。日常开发中也可用bun run build:development/build:preview/build:production脚本封装,构建到达 App Store Connect 后用bun run submit:production提交元数据与最新构建(见 apps/mobile/README.md)。
构建到达 App Store Connect 后,按以下顺序收尾:
- 确认配套的移动端 API 变更已部署到生产环境。
- 通过 TestFlight 在真机 iPhone 上安装该确切构建。
- 测试:全新安装、Google/Apple/邮箱/SSO 登录、录制权限、五分钟边界、后台与中断上传、导入、播放、评论、分析、现有 Pro 识别、用两个一次性账号测试举报与拉黑、用一个一次性账号测试账户删除。
- 核对应用图标、隐私清单、出口合规状态、截图、元数据、隐私答案、年龄分级、审核账号与审核备注。
- 审核通过后手动提交、手动发布。
发布后的维护:O2A 更新与构建边界
生产构建之外,仓库还预置了 OTA 更新通道:bun run update:preview -- --message "..."先预览发布,验证后再bun run update:production -- --message "..."发布到生产。更新按 channel 与 app 版本隔离;当原生依赖或 Expo 配置发生变化时,必须在 app.config.js 中递增version并发布新的生产构建,而不是发布不兼容的 OTA 更新——这与runtimeVersion.policy: "appVersion"的运行时隔离机制相辅相成。整套发布方法论(阻塞项清单、隐私问卷、审核备注、最终验证序列)同样适用于任何基于 Expo + EAS 的 iOS 应用,可作为团队 iOS 上线的标准操作流程模板。
【免费下载链接】CapOpen source Loom alternative. Beautiful, shareable screen recordings.项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考