一次完整的交付链路

把自己写的 App 装到别人手机上,中间要跨过四道关:工程要能构建、代码要能验证、构建产物要能签名、上架材料要过审核。很多人第一次提交卡在第三道和第四道关上,原因往往不是代码写错,而是对整条链路缺乏整体认知,于是在某个环节反复试错。

上架不是开发的最后一步,而是工程能力的一次体检:签名、测试、隐私清单,任何一项含糊,都会在审核环节被打回。
链路全景
  • 开发期工程结构 + 依赖管理 + 本地调试,重点是能稳定构建。
  • 验证期单元测试与 UI 测试跑在 CI 上,防止回归。
  • 签名期证书、描述文件、Entitlements 三者匹配,产出自签名归档包。
  • 发布期建版本 → 上传 → TestFlight 内测 → 提交审核 → 灰度发布。
iOS 开发全流程配图:抽象智能手机轮廓与开发工具卡片、上升箭头
从工具到上线的那条上升箭头,是靠工程配置与流程规范一步步搭出来的。

工程结构与依赖管理

一个可维护的 iOS 工程按「功能模块 + 基础设施」切分,而不是按文件类型堆在一起。常见约定是:App/ 放入口与依赖装配,Features/ 按业务域建立子目录,Core/ 放网络、存储、工具等公共能力,Resources/ 放资源与本地化文件。这样切分的好处是删除功能时可以整目录移除。

依赖管理首选 Swift Package Manager,它已经集成进 Xcode,版本锁定、分支依赖与二进制目标都能表达。老项目里的 CocoaPods 可以继续用,但新项目除非依赖只提供 Pod,否则不建议再引入——多一套构建系统就多一份排查成本。

结构约定
  • 一个功能一个目录界面、模型、服务放一起,删功能时整目录移除。
  • 依赖方向单向Features 可依赖 Core,Core 绝不反向依赖。
  • 环境配置分离用 xcconfig 区分 Debug / Release / Staging。

签名、证书与描述文件

iOS 的签名机制本质是一条信任链:开发者证书证明「你是谁」,App ID 定义「哪个应用」,描述文件把二者与设备列表、权限(Entitlements)绑定在一起。三者任意一项对不上,Xcode 就会报签名错误。日常开发建议交给自动签名:在 Signing & Capabilities 里勾选自动管理并选定 Team,Xcode 会负责申请证书与生成描述文件。

持续集成环境需要在命令行完成同样的动作,下面是归档与导出安装包的标准写法:

# 产出一个用于分发的归档包
xcodebuild archive -project Demo.xcodeproj -scheme Demo \
  -configuration Release -destination "generic/platform=iOS" \
  -archivePath build/Demo.xcarchive

# 用 ExportOptions.plist 导出 ipa(method 可选 app-store / ad-hoc)
xcodebuild -exportArchive -archivePath build/Demo.xcarchive \
  -exportOptionsPlist ExportOptions.plist -exportPath build/ipa

把 ExportOptions.plist 纳入版本管理,写清 method、teamID 与签名方式,可以让「本机能打包、流水线打不了」这类问题彻底消失,也让发布流程可以被完整复现。

测试:单元测试与 UI 测试

iOS 的测试分两层:XCTest 跑逻辑,速度快、定位准;XCUITest 跑真实界面交互,慢但能覆盖集成路径。合理的比例是业务逻辑全部有单元测试,关键用户旅程各留一条 UI 测试。测试不是为了覆盖率数字,而是为了在你改动公共代码时能立刻知道有没有踩坏别处。

import XCTest
@testable import Demo

final class CartTests: XCTestCase {
    func testAddIncreasesTotal() {
        let cart = CartModel()
        cart.add("SwiftUI 实战")
        cart.add("并发编程")
        XCTAssertEqual(cart.items.count, 2)
        XCTAssertEqual(cart.total, 20)
    }
}

@testable import 让我们能访问模块内部类型,但它只在 Debug 配置下生效,要注意别让测试辅助代码泄漏到发布版本。异步逻辑用 async 测试方法配合 await,不必再用基于期望值(XCTestExpectation)的老写法,后者既啰嗦又容易在超时上出问题。

打包、TestFlight 与上架审核

归档成功后可以用 Xcode 的 Organizer 面板上传,也可以走命令行。上传完成后版本会先出现在 TestFlight 中,此时可以邀请内测人员验证,而不必先通过审核。这是把「开发完成」和「发布上线」解耦的关键一步。

提交审核前要准备的材料包括:应用描述与关键词、多尺寸截图、隐私清单(PrivacyInfo.xcprivacy,声明数据收集类型与用途)、以及审核备注。如果登录是必需步骤,务必在其中提供可用的测试账号,否则审核人员无法验证功能,大概率被拒。

上架检查表
  • 权限文案相机、定位、麦克风必须说明具体场景,不能写「为了更好的体验」。
  • 账号删除提供注册的 App 必须同时提供应用内删除账号入口。
  • 隐私标签商店里的隐私标签要与实际采集行为、隐私清单三者一致。
  • 无占位内容审核期间不能崩溃,也不能出现「敬请期待」这类空页面。

提交后常见状态是等待审核、正在审核、被拒与可供销售。被拒时不要急着改代码再提交一次,先在 Resolution Center 里按条回应,沟通效率通常比盲目重试高得多。