news 2026/9/13 12:11:03

Cap Mobile iOS 开发指南:Expo Router、EAS 构建与 OTA 更新全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cap Mobile iOS 开发指南:Expo Router、EAS 构建与 OTA 更新全流程

Cap Mobile iOS 开发指南:Expo Router、EAS 构建与 OTA 更新全流程

【免费下载链接】CapOpen source Loom alternative. Beautiful, shareable screen recordings.项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap

Cap Mobile 是开源屏幕录制项目 Cap 的 iPhone 客户端,基于 Expo SDK 55 + Expo Router 构建,支持相机/麦克风录制、媒体导入、上传分享、评论与观看分析等功能。本文以 apps/mobile/README.md 为核心骨架,结合仓库中的package.jsonapp.config.jseas.jsonstore.config.jsonscripts/下的自动化脚本,系统讲解从本地开发、真机联调、EAS 一次性初始化,到模拟器/内部/生产构建、App Store 提交以及 OTA(Over-The-Air)更新的完整实战流程,读完即可独立跑通一条 iOS 移动端发布流水线。

说明:Cap Mobile 是 iPhone-only 的 Expo 应用,原生工程由 Expo 本地生成或由 EAS 远程生成,不会提交到 Git 仓库;除特别注明外,所有命令都在apps/mobile目录下执行。

一、项目架构与技术栈

在深入命令之前,先理解这个客户端是如何被组织的,这有助于解释后续各命令为什么这么设计。

1.1 Expo + 连续原生生成(CNG)

仓库 apps/mobile/README.md 明确指出:Cap Mobile 是一个使用Expo RouterContinuous Native Generation(连续原生生成)构建的 iOS Expo 应用,原生工程由本地expo prebuild或 EAS 生成,不进版本库。这一点在 apps/mobile/package.json 中可以得到印证——没有ios/目录的固定工程文件,只有prebuild:ios脚本(expo prebuild --platform ios --no-install)。

核心技术栈(来自 apps/mobile/package.json):

技术版本用途
expo~55.0.29运行时基础框架
expo-router~55.0.18文件路由(mainexpo-router/entry
react-native0.83.10原生渲染引擎
react19.2.0UI 框架
expo-camera~55.0.22相机与麦克风录制
expo-video~55.0.20原生播放
expo-dev-client~55.0.38开发客户端
expo-updates~55.0.27OTA 更新
expo-secure-store~55.0.17账号密钥安全存储
expo-apple-authentication~55.0.16Sign in with Apple
@shopify/flash-list2.0.2高性能视频列表
effect^3.18.4类型安全的副作用管理(业务层)

1.2 路由与功能模块

Expo Router 的文件路由入口位于 apps/mobile/app/_layout.tsx 的根布局:应用启动后先展示加载屏,未登录时渲染SignInPanel登录面板,已登录则进入 Stack 导航,挂载(tabs)(My Caps 列表)、caps/[id](Cap 详情)、analyticsorganization-settingsloom-import等页面。根布局还通过AuthProviderRecordingUploadProvider提供了全局的登录态与上传进度管理,并在屏幕底部常驻RecordingUploadStatus显示上传状态。

三个主 Tab 定义在 apps/mobile/app/(tabs)/_layout.tsx/_layout.tsx):index(My Caps 个人库)、upload(隐藏路由,作为录制/上传入口)、account(账户设置)。个人库页面(apps/mobile/app/(tabs)/index.tsx/index.tsx))使用FlashList渲染 Cap 卡片,并支持文件夹、空间切换、分享、密码设置、保存到相册等操作。

录制页 apps/mobile/app/record.tsx 聚合了CapRecorderView(相机录制原生模块,位于 apps/mobile/modules/cap-recorder)与CapScreenRecorderView(屏幕录制扩展,位于 apps/mobile/modules/cap-screen-recorder),并内置提词器(Teleprompter)。屏幕录制功能通过 App Group 与 Broadcast Extension 实现,其配置见下文app.config.js中的cap-screen-recorder插件。

1.3 两个原生自定义模块

  • cap-recorder:Swift 实现的相机录制模块(含.podspec),提供CapRecorderView及录制事件回调,供record.tsx使用。
  • cap-screen-recorder:屏幕录制模块,包含 Swift 源码、Broadcast Extension 的.plist/.entitlementsapp.plugin.js配置插件,用于 iPhone 屏幕录制(注意:当前 iPhone 端以相机录制为主,屏幕录制为扩展能力)。

二、本地开发:四条命令的完整脉络

README 给出了四条本地开发命令,它们的关系可以用一句话概括:决定"要不要启动 Web 后端"与"跑在模拟器还是真机"这两个维度

2.1 命令矩阵与根级脚本

命令后端目标设备用途
bun run dev:mobile启动(docker + @cap/web)iOS 模拟器标准全栈开发
bun run dev:mobile:physical启动真机 iPhone全栈 + 真机联调
bun run dev不启动(复用已运行后端)模拟器仅移动端开发
bun run dev:physical不启动真机真机 + 已有后端

前两个命令定义在仓库根目录的 package.json 中:dev:mobile会先docker:up拉起依赖容器、以trap保证退出时docker:stop,随后通过 Turbo 同时运行@cap/web@cap/mobile两个 workspace;dev:mobile:physical额外设置CAP_MOBILE_DEVICE=physical,让移动端脚本走真机分支。

后两个命令来自 apps/mobile/package.json 的dev/dev:physical脚本,二者都会先执行scripts/prepare-ios-development.mjs,再根据CAP_MOBILE_DEVICE环境变量选择调用run-ios-simulator.mjsrun-ios-device.mjs

// apps/mobile/package.json(节选) "dev": "CAP_MOBILE_DISABLE_ASSOCIATED_DOMAINS=1 CAP_MOBILE_BUILD_REACT_NATIVE_FROM_SOURCE=1 sh -c 'node scripts/prepare-ios-development.mjs && if [ \"${CAP_MOBILE_DEVICE:-simulator}\" = \"physical\" ]; then node scripts/run-ios-device.mjs; else node scripts/run-ios-simulator.mjs; fi'", "dev:physical": "CAP_MOBILE_DEVICE=physical bun run dev"

2.2 prepare-ios-development.mjs:预构建与依赖安装

scripts/prepare-ios-development.mjs 做两件事:

  1. 执行expo prebuild --platform ios --no-install生成(或刷新)原生 iOS 工程;
  2. 比较ios/Podfile.lockios/Pods/Manifest.lock,只要存在差异、或包含React-Core-prebuilt/ReactNativeDependencies、或缺少ExpoAppleAuthentication,就自动执行pod install --project-directory=ios安装 CocoaPods 依赖。

这样每次开发前都能自动保证原生工程与依赖是同步的。脚本支持CAP_MOBILE_DRY_RUN=1干跑模式,只打印命令不执行,便于排查。

2.3 run-ios-simulator.mjs:模拟器选择与容错

scripts/run-ios-simulator.mjs 通过xcrun simctl list devices available --json枚举可用 iPhone 模拟器,选择顺序为:

  1. IOS_SIMULATOR_UDID环境变量指定的设备;
  2. IOS_SIMULATOR_DEVICE环境变量指定的名称;
  3. 已 Booted 的模拟器;
  4. 名称包含 "Pro" 的型号;
  5. 列表中的第一个可用设备。

选定后会确保模拟器完成启动(simctl boot+bootstatus -b等待),再执行expo run:ios --device <udid>。值得注意的是脚本的容错设计:如果 Expo 启动过程中模拟器意外退出,它会自动重新启动并重试一次。

另外,当CAP_MOBILE_DISABLE_ASSOCIATED_DOMAINS=1且检测到工程中已有 Associated Domains 相关 entitlement 时,会先执行一次expo prebuild --clean重新生成工程,确保开发构建不携带生产关联域名。

2.4 run-ios-device.mjs:真机联调的网络自动发现

真机调试最麻烦的是"iPhone 访问 Mac 上的 API 与 Metro"。仓库的解决方案在 scripts/run-ios-device.mjs 与 scripts/mobile-development-network.mjs 中:

  • 优先使用CAP_MOBILE_DEVICE_API_URL(如果显式指定);
  • 否则通过findLanAddress()探测 Mac 的私网 IPv4 地址——按en0en1en2优先,自动跳过awdlbridgedockerlotailscaleutunvboxvmnet等虚拟网卡;
  • 找到后组合为http://<LAN_IP>:3000作为后端地址,并通过环境变量注入:EXPO_PUBLIC_CAP_WEB_URL=<apiBaseUrl>REACT_NATIVE_PACKAGER_HOSTNAME=<LAN_IP>(后者让 Metro 也走局域网地址);
  • 端口可通过CAP_MOBILE_LOCAL_API_PORT覆盖(默认 3000),局域网 IP 可通过CAP_MOBILE_LAN_IP覆盖。

如果既找不到局域网地址、也没有EXPO_PUBLIC_CAP_WEB_URL,脚本会报错并提示"请显式设置CAP_MOBILE_DEVICE_API_URL"。

三、一次性 EAS 初始化

从本地跑通到云端构建,需要把项目与 Expo 的 EAS(Expo Application Services)项目关联起来。

3.1 project:init 关联 EAS 项目

需要拥有对cap-software-incExpo 组织(或你自己的组织)有权限的 Expo 账号,执行:

bunx eas-cli@21.0.2 project:init

该命令会把应用关联到 EAS 项目,并生成一个公开的项目 ID。这个 ID 会被写入 apps/mobile/app.config.js:

const projectId = process.env.EXPO_PROJECT_ID ?? "616ebd7a-e876-4b21-82be-d626028042f6";

3.2 项目 ID 的用处

在 apps/mobile/app.config.js 中,projectId 被用于两处:

updates: projectId ? { url: `https://u.expo.dev/${projectId}`, // expo-updates 的 OTA 更新端点 } : undefined, // ... extra: { apiBaseUrl: process.env.EXPO_PUBLIC_CAP_WEB_URL ?? "https://cap.so", eas: projectId ? { projectId } : undefined, },
  • updates.url:OTA 更新的服务端点,指向该项目的 Expo Updates 服务;
  • extra.eas.projectId:运行时(含 expo-dev-client)用于识别所属 EAS 项目。

该项目 ID 是公开标识符,README 特别强调它"是 EAS Build 与 EAS Update 所必需的",提交到仓库是安全的。

3.3 后端地址与关联域名

  • EXPO_PUBLIC_CAP_WEB_URL:后端 API 基地址,默认生产环境为https://cap.so。需要在 development / preview / production 三个 EAS 环境分别配置,当某个 profile 需要指向非生产后端时(例如预发布环境)覆盖即可;
  • CAP_MOBILE_ASSOCIATED_DOMAINS:逗号分隔的关联域名列表(用于 Universal Links / Associated Domains),仅在显式设置时才会写入 iOS 工程的associatedDomains
  • CAP_MOBILE_BUILD_REACT_NATIVE_FROM_SOURCE=1:从源码构建 React Native(开发 profile 开启)。

3.4 其他关键配置速览

apps/mobile/app.config.js 中还包含大量值得了解的配置项:

配置值/行为说明
bundleIdentifierso.cap.mobileiOS Bundle ID
appleTeamId47B7FCLL43发布团队 ID
version1.0.0应用版本(OTA 更新按此隔离)
runtimeVersionpolicy: "appVersion"运行时版本跟随应用版本
schemecap深链 scheme
platforms["ios"]仅 iOS(当前无 Android)
usesNonExemptEncryptionfalse声明无豁免出口加密限制
usesAppleSignIntrue启用 Sign in with Apple
supportsTabletfalse仅手机,不支持 iPad
userInterfaceStylelight浅色模式
experiments.typedRoutestrue类型化路由(编译期校验路由)

权限文案也已配置好:相册读取("Cap imports videos from Photos for upload.")、相册写入("Cap saves downloaded videos to Photos.")、相机/麦克风("Allow Cap to use your camera/microphone while recording videos.")、Face ID("Allow Cap to protect your account key.")。字体资源(NeueMontreal 三字重)、启动屏(apps/mobile/assets/splash-icon.png)与图标(apps/mobile/assets/icon.png)同样在 config 中注册。

3.5 iOS 签名凭据

EAS 负责远程管理 iOS 签名凭据。首次真机(preview)或生产构建时,EAS 会要求被授权的 Apple Developer 账号创建或选择:分发证书(Distribution Certificate)Provisioning ProfileApp Store Connect API Key。也就是说,签名凭据不需要也不应该出现在仓库中。

四、三种构建 profile 与 App Store 提交

apps/mobile/eas.json 定义了三种构建 profile,对应的 npm script 与用途如下:

4.1 development:模拟器开发客户端

bun run build:development # 等价于 bunx eas-cli@21.0.2 build --platform ios --profile development

profile 特性:developmentClient: true(开发客户端,可连接 Metro)、distribution: "internal"simulator: true(纯模拟器构建,无需签名)、channel 为development,并在构建环境注入CAP_MOBILE_BUILD_REACT_NATIVE_FROM_SOURCE=1

本地开发并不强制需要 EAS 构建——bun run dev配合本地expo run:ios即可。development 构建适用于需要完整原生依赖打包、或 CI 分发开发构建的场景。

4.2 preview:内部真机构建

bun run build:preview

profile 特性:developmentClient未开启、distribution: "internal"(通过 TestFlight 或 Ad Hoc 分发给内部测试者)、channel 为preview。这是首次需要签名凭据的构建类型,EAS 会引导创建或选择证书与 profile。

4.3 production:生产构建与版本号管理

bun run build:production

profile 特性:autoIncrement: true生产构建号由 EAS 自动管理并递增)、credentialsSource: "remote"(凭据由 EAS 远程托管)、distribution: "store"(面向 App Store 分发)、channel 为production

eas.jsoncli.appVersionSource: "remote"也表明应用版本号来源是 EAS 远程管理,而不是仓库内的静态版本。

4.4 提交到 App Store Connect

构建通过发布验证后,提交最新生产构建与store.config.json中的元数据:

bun run submit:production # 等价于 bunx eas-cli@21.0.2 submit --platform ios --profile production --latest

提交配置(apps/mobile/eas.json 的submit.production)包含:appleTeamId: "47B7FCLL43"appName: "Cap"bundleIdentifier: "so.cap.mobile"companyName: "Cap Software, Inc."、语言en-US、SKUcap-mobile-ios,以及元数据路径./store.config.json

apps/mobile/store.config.json 提供了完整的商店文案:应用名 "Cap"、副标题 "Record, share, collaborate"、分类PRODUCTIVITYPHOTO_AND_VIDEO、关键词(camera recorder、async video、video sharing、analytics 等),并声明automaticRelease: false(构建上传后手动发布)。

五、OTA 更新:preview 先行,production 兜底

5.1 先发 preview 验证

bun run update:preview -- --message "Describe the update" # 等价于 bunx eas-cli@21.0.2 update --channel preview --environment preview --message "..."

将当前提交的 JavaScript 与静态资源更新发布到preview通道,供内部测试者先验证。

5.2 验证后发 production

bun run update:production -- --message "Describe the update" # 等价于 bunx eas-cli@21.0.2 update --channel production --environment production --message "..."

5.3 更新隔离规则

README 明确指出两条关键约束:

  1. 按通道隔离:更新只影响对应 channel 的客户端,preview 的验证结果不影响 production 用户;
  2. 按应用版本隔离:OTA 更新不能跨越原生版本。当原生依赖或 Expo 配置变化时,必须递增 apps/mobile/app.config.js 中的version,并发布一个新的生产构建,而不是推送一个不兼容的 OTA 更新。

结合runtimeVersion: { policy: "appVersion" }的配置可以理解其机制:expo-updates 以应用版本作为运行时版本标识,因此版本不变时 JS 更新可以热推,版本改变后旧的 OTA 更新包会自动失效,必须走新的原生构建。

六、从开发到上线的完整流水线

综合 README 与仓库脚本,一条完整的移动端发布流水线如下:

# 1. 本地全栈开发(模拟器) bun run dev:mobile # 2. 真机联调(自动发现 Mac 局域网地址) bun run dev:mobile:physical # 3. 一次性关联 EAS 项目(首次) bunx eas-cli@21.0.2 project:init # 4. 构建三种 profile bun run build:development # 模拟器开发客户端 bun run build:preview # 内部真机构建 bun run build:production # 生产构建(构建号由 EAS 自动递增) # 5. OTA 更新(先 preview 验证) bun run update:preview -- --message "Describe the update" bun run update:production -- --message "Describe the update" # 6. 提交 App Store bun run submit:production

版本策略小结

变更类型操作
JS / 资源 / 业务逻辑改动bun run update:preview验证 →bun run update:production发布 OTA
原生依赖 / Expo 配置 / 原生模块改动递增app.config.js中的versionbun run build:productionbun run submit:production

七、上线质量保障(仓库提供的实测依据)

虽然 README 未展开,但仓库中的 apps/mobile/app-store-release.md 给出了 1.0 版本发布前的完整核验清单,可作为"版本发布"章节的实践佐证:

  • 基础验证:Expo Doctor 全部 19 项检查通过;Expo SDK 55 各包版本对齐;TypeScript 校验通过;移动端测试套件通过 34 个文件、214 个测试(对应 apps/mobile/package.json 的typechecktest脚本);
  • 构建验证:clean prebuild、CocoaPods install、生产 JS 导出、Xcode Release 模拟器构建全部通过,构建产物能在 iPhone 17 Pro Max 模拟器上从内嵌生产 bundle 正常启动;
  • 合规验证:非豁免加密声明为 false、包含聚合隐私清单、无广告 SDK、不请求 App Tracking Transparency 权限、默认不启用 Associated Domains;
  • 功能边界:免费账号最多录制 5 分钟,1.0 版本不在应用内售卖数字功能(不提供外部 Stripe 结账),现有 Cap Pro 订阅在其他平台购买的权益会在账户页被识别;
  • 上线前核对命令(在apps/mobile下执行):
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

八、常见问题与排查建议

Q1:真机开发时 iPhone 连不上后端?检查CAP_MOBILE_DEVICE_API_URL是否显式设置;确认 Mac 与 iPhone 在同一局域网;必要时用CAP_MOBILE_LAN_IP指定局域网 IP、用CAP_MOBILE_LOCAL_API_PORT指定端口(默认 3000)。网络发现逻辑见 scripts/mobile-development-network.mjs。

Q2:pod install一直不执行或依赖不同步?开发前会自动比对ios/Podfile.lockios/Pods/Manifest.lock;若提示依赖不一致,可删除本地ios/目录后重新执行bun run dev(会重新 prebuild),或用bun run prebuild:ios手动触发。

Q3:OTA 更新发布后用户没收到?先确认发布到的 channel 与客户端构建时的 channel 一致(development / preview / production);再确认没有跨版本发布——原生依赖变更后必须提升app.config.jsversion并重新走生产构建。

Q4:找不到合适的模拟器?run-ios-simulator.mjs支持用IOS_SIMULATOR_UDIDIOS_SIMULATOR_DEVICE指定目标设备,否则按"已启动 → 含 Pro → 第一个可用"的顺序自动选择。

Q5:构建失败卡在签名?preview 与 production 构建依赖 EAS 远程签名凭据,需确认 Expo 账号有权限、Apple Developer 账号已完成证书创建授权;本地模拟器开发构建(development profile 且simulator: true)不需要签名。

结语

Cap Mobile 用"Expo Router + CNG + EAS + expo-updates"搭建了一条典型的现代 iOS 应用开发与发布流水线:本地通过 scripts/ 下的脚本自动完成 prebuild、Pod 安装、模拟器选择与真机网络发现;云端通过 apps/mobile/eas.json 的三种 profile 覆盖开发、内部测试与生产分发;日常迭代用 channel 隔离的 OTA 更新热推 JS 与资源,原生变更则回归构建 + 提交。文中所有命令与配置均可在仓库的 apps/mobile 目录下找到对应实现,按本文流程即可从零跑通 Cap Mobile 的完整 iOS 发布闭环。

【免费下载链接】CapOpen source Loom alternative. Beautiful, shareable screen recordings.项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 12:10:04

Spring Boot健康检查与监控实践指南

1. Spring Boot健康检查与监控概述在微服务架构中&#xff0c;服务健康状态监控是保障系统稳定性的关键环节。Spring Boot通过Actuator模块提供了开箱即用的健康检查能力&#xff0c;让开发者能够快速构建完善的监控体系。我在多个生产项目中实践发现&#xff0c;合理的健康检查…

作者头像 李华
网站建设 2026/9/13 12:09:33

英飞凌CoolGaN量产方案:驱动-器件协同设计实战指南

1. 项目概述&#xff1a;为什么英飞凌这次发布的不是“概念样品”&#xff0c;而是真正能上产线的氮化镓方案&#xff1f; 最近在电源设计圈里&#xff0c;好几个老同事发来消息问&#xff1a;“听说英飞凌新推的CoolGaN方案能直接量产了&#xff1f;是不是真的不用再自己搭驱动…

作者头像 李华
网站建设 2026/9/13 12:09:31

彻底卸载OpenClaw:进程、配置、Docker数据卷一网打尽

如果你电脑上装过 OpenClaw&#xff08;就是大家常说的“龙虾”&#xff09;&#xff0c;应该能感受到它是个相当能折腾的 AI 智能体框架——接微信、挂网关、调度各种模型&#xff0c;确实好玩。但等你想卸载的时候&#xff0c;才是真正头疼的开始&#xff1a;命令行里敲which…

作者头像 李华
网站建设 2026/9/13 12:08:46

软硬件协同设计实现无人机低功耗优化

1. 项目概述&#xff1a;当无人机芯片开始“省电模式”&#xff0c;MIT团队做对了什么&#xff1f;低功耗不是靠调低电压、关几个外设就完事的——那是硬件工程师的直觉&#xff0c;不是系统级的解法。真正让小型无人机续航翻倍、发热骤降、飞行更稳的&#xff0c;是软硬件之间…

作者头像 李华