分析日期:2026-08-06
示例工程:
WebRTC-iOS-main重点概念:Project、Target、Workspace、Scheme、Build Configuration、 Build Settings、Build Phases、Package Dependencies、签名与构建产物
配套阅读:
1. 先建立一套正确的心智模型
iOS 工程不是"一个文件夹加一些 Swift 文件"。从工程化角度看,它是一张由 容器、构建规则、依赖和执行配置组成的图。
可以先记住下面六句话:
- Workspace 是多个工程的工作容器。
- Project 保存 Target、文件引用和构建配置。
- Target 描述如何把一组输入构建成一个 Product。
- Scheme 描述 Build、Run、Test、Profile、Analyze、Archive 怎么执行。
- Build Configuration 是一套有名字的 Build Settings。
- Package Dependency 向 Target 提供可编译或可链接的 Product。
它们的基本组合关系:
text
Workspace
│
├── Project A
│ ├── Target: App
│ ├── Target: UnitTests
│ ├── Target: UITests
│ └── Package References
│
├── Project B
│ └── Target: Framework
│
└── Schemes
├── App-Debug
├── App-Staging
└── App-Release
构建关系可以简化为:
text
Scheme
-> 选择 Build Action
-> 选择一个或多个 Target
-> 为每个 Action 选择 Build Configuration
-> Target 读取 Build Settings
-> 执行 Build Phases
-> 构建 Target Dependencies 和 Package Products
-> 生成 Product
1.1 当前 WebRTC Demo 的实际模型
text
WebRTC-Demo.xcworkspace
│
├── WebRTC-Demo.xcodeproj
│ └── Target: WebRTC-Demo
│ ├── Product: WebRTC-Demo.app
│ ├── Sources
│ ├── Resources
│ ├── Package Product: Starscream
│ └── Package Product: WebRTC
│
└── SignalingServer.xcodeproj
└── Target: SignalingServer
├── Product: SignalingServer
└── Platform: macOS
运行时还有另一层关系:
text
WebRTC-Demo.app -- WebSocket --> SignalingServer
这个 WebSocket 关系不属于 Target 编译依赖,而属于两个进程之间的运行时服务 依赖。
2. Xcode 左侧目录不完全等于磁盘目录
学习工程化之前,需要先区分 Xcode 展示和磁盘真实结构。
2.1 磁盘文件系统
Finder 或 Terminal 看到的是实际文件:
text
WebRTC-iOS-main/
├── WebRTC-Demo-App/
│ ├── Sources/
│ ├── Resources/
│ ├── SupportingFiles/
│ ├── WebRTC-Demo.xcodeproj/
│ └── WebRTC-Demo.xcworkspace/
└── signaling/
└── Swift/
└── SignalingServer.xcodeproj/
2.2 Xcode Navigator
Xcode Project Navigator 展示的是:
- Workspace 引用的 Project。
- Project 保存的 Group。
- 文件引用。
- Package Dependencies。
- Products。
因此 Xcode 中的节点可能是:
- 真实目录。
- 逻辑 Group。
- 外部 Project 引用。
- Package 的虚拟展示。
- 编译产物引用。
不能只根据缩进判断磁盘从属关系。
2.3 Group 和 Folder Reference
传统 Xcode 工程常见黄色 Group:
- 主要用于逻辑组织。
- Group 名称和磁盘目录可以不同。
- 移动 Group 不一定移动磁盘文件。
- Group 中出现文件不代表文件一定参与构建。
Folder Reference 或新版本 Xcode 的同步文件夹更接近磁盘目录,但仍需要确认:
- Target Membership。
- Build Phase。
- 资源处理方式。
2.4 文件出现不等于被编译
一个 .swift 文件即使显示在 Xcode 左侧,如果没有加入某个 Target 的 Compile Sources,它就不会进入该 Target。
反过来,同一个文件可以属于多个 Target,并针对每个 Target 分别编译。
这是 Target Membership 的本质。
3. Project 是什么
3.1 Project 的职责
.xcodeproj 是 Xcode 工程容器,主要保存:
- 文件和 Group 引用。
- Target。
- Product。
- Build Phases。
- Build Settings。
- Build Configurations。
- Target Dependencies。
- Package References。
- 共享 Scheme。
- 工程级元数据。
它不是最终 App,也不是一个普通目录。
3.2 .xcodeproj 实际是目录包
在 Finder 中看起来像单个文件:
text
WebRTC-Demo.xcodeproj
实际是一个目录包,内部包含:
text
WebRTC-Demo.xcodeproj/
├── project.pbxproj
├── project.xcworkspace/
├── xcshareddata/
│ └── xcschemes/
└── xcuserdata/
关键文件:
| 文件 | 作用 | 是否通常提交 |
|---|---|---|
project.pbxproj |
Target、文件引用、Build Settings 等核心配置 | 是 |
xcshareddata/xcschemes |
团队共享 Scheme | 是 |
xcuserdata |
某个开发者的本地 UI 和 Scheme 状态 | 否 |
project.xcworkspace |
单独打开 Project 时的隐式 Workspace | 视内容而定 |
3.3 project.pbxproj 保存了什么
当前 project.pbxproj 是 OpenStep 风格的文本对象图。
常见对象:
| 对象 | 含义 |
|---|---|
PBXProject |
Project 根对象 |
PBXNativeTarget |
原生构建 Target |
PBXFileReference |
文件或 Product 引用 |
PBXGroup |
Xcode 中的逻辑分组 |
PBXBuildFile |
某个文件进入某个 Build Phase 的记录 |
PBXSourcesBuildPhase |
Compile Sources |
PBXResourcesBuildPhase |
Copy Bundle Resources |
PBXFrameworksBuildPhase |
Link Binary With Libraries |
PBXCopyFilesBuildPhase |
文件复制阶段 |
PBXTargetDependency |
Target 间构建依赖 |
XCBuildConfiguration |
Debug/Release 等设置集合 |
XCConfigurationList |
Project 或 Target 的 Configuration 列表 |
XCRemoteSwiftPackageReference |
远端 Swift Package 声明 |
XCSwiftPackageProductDependency |
使用 Package 中的 Product |
3.4 UUID 如何连接对象
project.pbxproj 中对象通过类似下面的 ID 互相引用:
text
79262EE220B0D6F600D576C1
例如 Scheme 中的:
xml
BlueprintIdentifier = "79262EE220B0D6F600D576C1"
指向 App Project 中的 WebRTC-Demo Target。
Xcode 就是通过这些 ID 将:
- Scheme。
- Target。
- Build Phase。
- File Reference。
- Product。
连接成一张对象图。
3.5 为什么不建议手工修改 pbxproj
它虽然是文本文件,但手工修改容易造成:
- UUID 引用断裂。
- 同一文件重复加入 Build Phase。
- Group 和磁盘路径不一致。
- Package Product 只加到 Project,未加到 Target。
- Git 合并冲突。
- Xcode 无法打开工程。
常规修改优先通过 Xcode UI 完成。
只有在明确理解对象图、需要修复冲突或执行可靠的自动化生成时,才直接编辑。
4. Project 级配置和 Target 级配置
Project 和 Target 都可以拥有 Build Configuration 与 Build Settings。
4.1 Project 级设置
Project 级设置适合放多个 Target 共用的默认值,例如:
- Swift 语言版本。
- 警告规则。
- Deployment Target 默认值。
- 编译器优化策略。
- 代码分析选项。
当前 App Project 的 Debug 设置包括:
text
SDKROOT = iphoneos
IPHONEOS_DEPLOYMENT_TARGET = 12.0
SWIFT_VERSION = 5.0
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG
SWIFT_OPTIMIZATION_LEVEL = -Onone
ENABLE_TESTABILITY = YES
Release 设置包括:
text
SWIFT_OPTIMIZATION_LEVEL = -O
SWIFT_COMPILATION_MODE = wholemodule
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
VALIDATE_PRODUCT = YES
4.2 Target 级设置
Target 级设置描述具体 Product,例如:
text
PRODUCT_BUNDLE_IDENTIFIER = com.stasel.WebRTC
PRODUCT_NAME = $(TARGET_NAME)
INFOPLIST_FILE = $(SRCROOT)/SupportingFiles/Info.plist
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
TARGETED_DEVICE_FAMILY = 1,2
CODE_SIGN_STYLE = Automatic
这些设置只属于 WebRTC-Demo App Target。
4.3 覆盖关系
可以把常见优先级理解为从默认到具体:
text
Xcode/平台默认值
-> Project Base Configuration(Project 关联的 xcconfig)
-> Project Build Settings(保存在 project.pbxproj)
-> Target Base Configuration(Target 关联的 xcconfig)
-> Target Build Settings(保存在 project.pbxproj)
-> 命令行或 CI 显式覆盖
同一层还可能有针对 SDK、架构或 Configuration 的条件值。Xcode 最终按上下文 解析这些设置,不能把所有场景简化为一条绝对规则。
实际解析还涉及条件设置,例如:
text
SETTING[sdk=iphoneos*]
SETTING[config=Debug]
SETTING[arch=arm64]
查看 Xcode 中 Levels 视图或执行 xcodebuild -showBuildSettings, 比猜测最终值更可靠。
4.4 $(inherited) 的意义
列表型设置常需要保留上层值:
text
OTHER_LDFLAGS = $(inherited) -ObjC
如果覆盖时省略 $(inherited),可能意外丢失:
- Project 级配置。
- Package 或 CocoaPods 注入值。
- 平台默认搜索路径。
5. Target 是什么
5.1 Target 的核心定义
Target 是一套"输入 + 规则 + 设置 = 输出"的构建配方。
text
Target
├── Inputs
│ ├── Swift/Objective-C/C++ 源码
│ ├── Assets、XIB、Storyboard
│ ├── Framework、Library
│ └── Package Product
├── Build Settings
├── Build Phases
├── Dependencies
└── Product
5.2 Target 不等于 App
App 只是 Target 的一种 Product 类型。
常见 Target:
| Target 类型 | 常见 Product |
|---|---|
| iOS Application | .app |
| Framework | .framework |
| Static Library | .a |
| Unit Testing Bundle | .xctest |
| UI Testing Bundle | .xctest |
| App Extension | .appex |
| Widget Extension | .appex |
| Notification Service Extension | .appex |
| Command Line Tool | 可执行文件 |
5.3 当前工程有两个 Target
WebRTC-Demo.xcodeproj:
text
Target: WebRTC-Demo
Product Type: application
Product: WebRTC-Demo.app
Platform: iOS
SignalingServer.xcodeproj:
text
Target: SignalingServer
Product Type: tool
Product: SignalingServer
Platform: macOS
两个 Target 位于不同 Project 中。
5.4 一个 Project 可以有多个 Target
常见正式 App Project:
text
CompanyApp.xcodeproj
├── CompanyApp
├── CompanyAppTests
├── CompanyAppUITests
├── NotificationServiceExtension
├── WidgetExtension
└── ShareExtension
它们可以复用文件,但有各自的:
- Product。
- Bundle Identifier。
- Info.plist。
- Entitlements。
- Build Settings。
- Build Phases。
- Deployment Target。
5.5 Target Membership
在 File Inspector 中勾选 Target Membership,本质上是在修改文件进入哪些 Target 的 Build Phase。
当前 WebRTCClient.swift 在 App Target 的 Compile Sources 中,所以会编译 进 App。
当前 WebSocketServer.swift 只在 SignalingServer Target 的 Compile Sources 中,所以不会编译进 iOS App。
5.6 同一个源文件属于多个 Target
如果 SharedLogger.swift 同时勾选 App 和 Extension:
text
SharedLogger.swift
├── 编译进 App Target
└── 再编译进 Extension Target
它不是运行时共享一份二进制,而是分别成为两个 Product 的一部分。
代码需要兼容两个 Target 的平台、SDK 和可用 Framework。
5.7 Target Dependency
Target Dependency 表示构建顺序和 Product 依赖。
例如:
text
App Target
-> FeatureFramework Target
-> CoreFramework Target
Xcode 会先构建 Core,再构建 Feature,最后构建 App。
当前 App Target 和 SignalingServer Target 的 dependencies 都为空。 所以它们没有构建依赖。
6. Target 依赖、链接依赖和运行时依赖
这是工程中最容易混淆的一组概念。
6.1 Target 构建依赖
text
App Target -> InternalFramework Target
含义:
- 构建 App 前先构建 Framework。
- App 通常还会链接该 Framework Product。
6.2 Framework/Library 链接依赖
text
App Target -> WebRTC Package Product
含义:
- App 源码可
import WebRTC。 - 链接器需要解析 WebRTC 符号。
- 动态 Framework 可能需要嵌入 App Bundle。
6.3 资源依赖
text
App Target -> Assets.xcassets / XIB / Storyboard
含义:
- 资源会被编译或复制到 App Bundle。
- 不参与 Swift 类型检查和链接。
6.4 运行时服务依赖
text
WebRTC-Demo.app -- ws://host:8080 --> SignalingServer
含义:
- App 构建不需要 Server Product。
- App 启动后需要网络上的 Server 正常运行。
- Server 不会被打包进
.app。
6.5 四类关系对比
| 关系 | 发生阶段 | Xcode 配置位置 | 当前示例 |
|---|---|---|---|
| Target Dependency | 构建前 | Target Dependencies | 当前为空 |
| Link Dependency | 链接时 | Frameworks/Package Product | WebRTC、Starscream |
| Resource Dependency | 资源处理时 | Copy Bundle Resources | Assets、XIB、Storyboard |
| Runtime Service | App 运行时 | 业务配置和网络协议 | SignalingServer |
7. Build Phases 是什么
7.1 Build Phases 决定构建步骤
Target 决定构建什么,Build Phases 描述按什么步骤处理输入。
常见阶段:
- Target Dependencies。
- Compile Sources。
- Link Binary With Libraries。
- Copy Bundle Resources。
- Embed Frameworks。
- Copy Files。
- Run Script。
Xcode 还会执行没有完全显示在列表中的底层任务,例如:
- 处理 Info.plist。
- 生成 Swift Module。
- 生成 dSYM。
- Code Sign。
- 验证 Product。
7.2 Compile Sources
当前 App Target 编译:
text
AppDelegate.swift
Config.swift
MainViewController.swift
VideoViewController.swift
WebRTCClient.swift
SignalingClient.swift
Message.swift
WebSocketProvider.swift
StarscreamProvider.swift
NativeWebSocket.swift
SessionDescription.swift
IceCandidate.swift
RTCStates.swift
如果文件没有进入这里:
- 文件不会参与该 Target 的 Swift 编译。
- 文件中的类型对该 Target 不存在。
- 即使 Navigator 能看到文件,也没有作用。
7.3 Link Binary With Libraries
当前 App Target 链接:
text
WebRTC
Starscream
源码编译完成后,链接器把:
- App 自身对象文件。
- Swift Runtime 需求。
- 系统 Framework。
- Package Product。
组合为可执行文件。
7.4 Copy Bundle Resources
当前 App Target 处理:
text
Assets.xcassets
MainViewController.xib
VideoViewController.xib
LaunchScreen.storyboard
资源不是简单全部原样复制:
- Asset Catalog 由
actool编译。 - XIB/Storyboard 由 Interface Builder 编译器处理。
- 本地化资源会按
.lproj组织。
7.5 Info.plist 为什么不应加入 Copy Bundle Resources
当前 Target 通过:
text
INFOPLIST_FILE = $(SRCROOT)/SupportingFiles/Info.plist
指定输入 Info.plist。
构建系统会展开:
text
$(PRODUCT_NAME)
$(PRODUCT_BUNDLE_IDENTIFIER)
$(EXECUTABLE_NAME)
并生成 Product 内最终的 Info.plist。
它是构建元数据输入,不是普通资源文件,因此不应再手动加入 Copy Bundle Resources。
7.6 Run Script
正式项目常用 Run Script:
- SwiftLint。
- SwiftFormat 检查。
- 代码生成。
- 上传 dSYM。
- License 汇总。
- 资源校验。
工程化注意事项:
- 声明 Input Files 和 Output Files。
- 避免脚本每次都运行。
- 不在脚本中写入源码目录的非确定性文件。
- 脚本失败应返回非零状态。
- 不把密钥直接写进脚本。
- 考虑
ENABLE_USER_SCRIPT_SANDBOXING。
当前 App Target 没有自定义 Run Script。
8. Product 是什么
8.1 Product 是 Target 的输出
text
Target + Configuration + SDK + Architecture
-> Product
同一个 Target 在不同条件下会产生不同构建变体:
text
Debug + iPhone Simulator + arm64
Release + iPhone Device + arm64
8.2 .app 是 Bundle
WebRTC-Demo.app 不是单个二进制文件,而是目录 Bundle,通常包含:
text
WebRTC-Demo.app/
├── WebRTC-Demo
├── Info.plist
├── Assets.car
├── Frameworks/
├── Base.lproj/
├── embedded.mobileprovision
└── _CodeSignature/
不同构建目的地和签名方式下内容会变化。
8.3 构建产物在哪里
默认位于 DerivedData:
text
~/Library/Developer/Xcode/DerivedData/
典型目录:
text
Build/Products/Debug-iphonesimulator/
Build/Products/Debug-iphoneos/
Build/Intermediates.noindex/
SourcePackages/
DerivedData 是可再生构建数据,不应提交到 Git。
9. Workspace 是什么
9.1 Workspace 的职责
.xcworkspace 是工作区容器,可以同时装载:
- 多个
.xcodeproj。 - 跨 Project Target 依赖。
- Swift Package 解析状态。
- Workspace 级共享设置。
- CocoaPods 生成的 Pods Project。
Workspace 自身通常不生成 Product,也没有一个"Workspace Target"。
9.2 当前 Workspace 如何引用两个 Project
xml
<FileRef location="group:WebRTC-Demo.xcodeproj">
</FileRef>
<FileRef
location="group:../signaling/Swift/SignalingServer.xcodeproj">
</FileRef>
因此 Xcode 同时显示:
text
WebRTC-Demo
SignalingServer
第二个 Project 位于父目录下的 signaling/Swift,不需要放进 App 目录。
9.3 Workspace 不会自动建立依赖
把两个 Project 放入同一 Workspace,只意味着 Xcode 可以同时看到它们。
不会自动发生:
- App 链接 Server。
- App 构建时启动 Server。
- Server 打包进 App。
- 两个 Target 自动建立依赖。
如果需要跨 Project 编译依赖,仍需明确添加 Target Dependency 和 Product 引用。
9.4 .xcodeproj 中为什么还有 project.xcworkspace
单独打开 .xcodeproj 时,Xcode 也会在内部使用隐式 Workspace:
text
WebRTC-Demo.xcodeproj/project.xcworkspace
它和显式外层:
text
WebRTC-Demo.xcworkspace
不是同一个容器。
这也是当前仓库出现两份 Package.resolved 的原因之一。
9.5 什么时候需要 Workspace
适合使用 Workspace 的场景:
- 多个 Xcode Project 协作。
- App + 内部 Framework Projects。
- CocoaPods。
- 需要跨 Project 调试。
- 需要统一打开 App、工具和服务项目。
单个简单 Project 也可以只用 Xcode 自动的隐式 Workspace。
9.6 当前工程应该打开什么
README 指定:
text
WebRTC-Demo-App/WebRTC-Demo.xcworkspace
团队和 CI 应统一使用这个入口,避免 Package 锁文件和 Scheme 可见性不一致。
10. Scheme 是什么
10.1 Scheme 是执行配方
Scheme 回答:
- Build 哪些 Target?
- Run 哪个可执行 Product?
- Test 哪些测试 Bundle?
- 使用哪个 Build Configuration?
- 启动时带什么参数和环境变量?
- 使用哪些诊断工具?
- Archive 哪个 Product?
Scheme 不包含业务源码,也不是编译产物。
10.2 Scheme 的六个主要 Action
| Action | 用途 | 常见 Configuration |
|---|---|---|
| Build | 构建选中的 Target | 由调用它的 Action 决定 |
| Run | 安装并启动 App | Debug |
| Test | 执行 Unit/UI Tests | Debug |
| Profile | 使用 Instruments 分析 | Release |
| Analyze | 静态分析 | Debug |
| Archive | 生成 Archive | Release |
10.3 当前 WebRTC-Demo Scheme
共享 Scheme 位于:
Build Action 只包含:
text
WebRTC-Demo.app
其配置:
text
Test -> Debug
Run -> Debug
Profile -> Release
Analyze -> Debug
Archive -> Release
Test Action 的 Testables 为空,说明当前工程没有配置测试 Target。
当前 Scheme 还设置:
text
parallelizeBuildables = YES
buildImplicitDependencies = YES
这允许无先后依赖的 Buildable 并行构建,并让 Xcode 根据链接关系推断部分依赖。 但是当前 Scheme 的 Build Action 只列出 WebRTC-Demo.app,SignalingServer 既不在 Build Action 中,也不是 App 的 Target Dependency,所以不会随 App 一起构建或启动。
10.4 Scheme 不等于 Target
一个 Target 可以被多个 Scheme 使用:
text
Target: CompanyApp
├── Scheme: CompanyApp-Debug
├── Scheme: CompanyApp-Staging
└── Scheme: CompanyApp-Release
一个 Scheme 也可以构建多个 Target:
text
Scheme: CompanyApp
├── App Target
├── Widget Target
└── InternalFramework Target
10.5 Scheme 和运行设备也不是一回事
Xcode 顶部通常同时选择:
text
Scheme + Destination
例如:
text
WebRTC-Demo + iPhone 16 Simulator
WebRTC-Demo + Chanpin's iPhone
SignalingServer + My Mac
Scheme 决定做什么,Destination 决定为哪个平台、设备和架构构建。
10.6 Shared Scheme 与 User Scheme
共享 Scheme:
text
xcshareddata/xcschemes/
特点:
- 可提交到 Git。
- 团队和 CI 可见。
- 正式构建 Scheme 应共享。
用户 Scheme 或本地状态:
text
xcuserdata/<user>.xcuserdatad/
特点:
- 只对当前用户有效。
- 不应作为 CI 依赖。
- 通常不提交。
当前 App Scheme 已共享。SignalingServer 可以由 Xcode 自动生成或管理 Scheme, 但正式团队使用时也应确认需要的 Scheme 已共享。
10.7 Scheme 可保存的高级配置
Run Action 可以设置:
- Launch Arguments。
- Environment Variables。
- Working Directory。
- StoreKit Configuration。
- GPU Frame Capture。
- Address Sanitizer。
- Thread Sanitizer。
- Main Thread Checker。
- Zombie Objects。
- Memory Graph。
不要把生产密钥直接写入共享 Scheme。
11. Build Configuration 是什么
11.1 Configuration 是有名字的设置集合
默认是:
text
Debug
Release
它们只是一组 Build Settings 的名称,不自动等于业务环境。
11.2 Debug 与 Release 的典型差异
Debug:
- 优化低,便于断点。
DEBUG编译条件。ENABLE_TESTABILITY = YES。- 构建更快。
- 通常保留更多调试信息。
Release:
- 优化更高。
- 生成 dSYM。
- 关闭部分断言。
- 执行 Product Validation。
- 更接近发布性能。
11.3 Debug 不等于测试环境
这几个维度是不同的:
| 维度 | 示例 |
|---|---|
| 优化级别 | Debug / Release |
| 后端环境 | Dev / Staging / Production |
| App 品牌 | Consumer / Enterprise |
| 发布渠道 | Internal / TestFlight / App Store |
正式项目可能设计:
text
Debug
Staging
Release
也可能使用:
text
Debug-Dev
Debug-Staging
Release-Staging
Release-Production
但 Configuration 数量过多会导致维护成本指数上升,应只保留真实需要的维度。
11.4 Scheme 如何选择 Configuration
当前 App Scheme:
text
Run -> Debug
Archive -> Release
因此点击 Run 和 Archive 使用的 Build Settings 不同。
如果创建 CompanyApp-Staging Scheme,可以设置:
text
Run -> Staging
Archive -> Staging
11.5 Project 和 Target 都有同名 Configuration
当前 Project 有:
text
Project Debug
Project Release
App Target 也有:
text
Target Debug
Target Release
构建 Target 的 Debug 变体时,会组合 Project Debug 和 Target Debug 的设置。
12. Build Settings 深入理解
12.1 Build Settings 是参数系统
Build Settings 最终会变成编译器、链接器、资源工具和签名工具的参数。
例如:
text
SWIFT_OPTIMIZATION_LEVEL = -Onone
影响 Swift 编译器优化。
text
PRODUCT_BUNDLE_IDENTIFIER = com.company.app
影响最终 Bundle Identifier、签名和安装身份。
12.2 常见设置分类
编译:
text
SWIFT_VERSION
SWIFT_OPTIMIZATION_LEVEL
SWIFT_ACTIVE_COMPILATION_CONDITIONS
OTHER_SWIFT_FLAGS
平台:
text
SDKROOT
IPHONEOS_DEPLOYMENT_TARGET
SUPPORTED_PLATFORMS
TARGETED_DEVICE_FAMILY
Product:
text
PRODUCT_NAME
PRODUCT_BUNDLE_IDENTIFIER
MACH_O_TYPE
INFOPLIST_FILE
链接:
text
OTHER_LDFLAGS
FRAMEWORK_SEARCH_PATHS
LIBRARY_SEARCH_PATHS
LD_RUNPATH_SEARCH_PATHS
签名:
text
CODE_SIGN_STYLE
DEVELOPMENT_TEAM
CODE_SIGN_ENTITLEMENTS
PROVISIONING_PROFILE_SPECIFIER
资源:
text
ASSETCATALOG_COMPILER_APPICON_NAME
DEVELOPMENT_ASSET_PATHS
12.3 Build Setting 变量替换
当前 Info.plist 使用:
xml
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
构建时会替换为:
text
com.stasel.WebRTC
常见变量:
text
$(SRCROOT)
$(PROJECT_DIR)
$(TARGET_NAME)
$(PRODUCT_NAME)
$(CONFIGURATION)
$(BUILT_PRODUCTS_DIR)
$(DERIVED_FILE_DIR)
12.4 条件 Build Settings
可以针对 SDK 或 Configuration 设置:
text
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
或在 xcconfig 中使用条件:
text
API_HOST[config=Debug] = dev-api.example.com
API_HOST[config=Release] = api.example.com
条件配置应控制在可理解范围内,复杂条件容易导致本地和 CI 行为不一致。
12.5 查看最终 Effective Settings
Xcode:
text
Target -> Build Settings -> Levels
命令行:
bash
xcodebuild -showBuildSettings \
-workspace WebRTC-Demo-App/WebRTC-Demo.xcworkspace \
-scheme WebRTC-Demo \
-configuration Debug
排查问题时要看最终值,不只看某一层输入值。
13. xcconfig 的工程化价值
13.1 什么是 xcconfig
.xcconfig 是文本形式的 Build Settings 文件。
示例:
text
SWIFT_VERSION = 5.10
IPHONEOS_DEPLOYMENT_TARGET = 16.0
PRODUCT_BUNDLE_IDENTIFIER = com.company.interview
Project 或 Target 的某个 Build Configuration 可以关联一个 Base Configuration。
13.2 为什么大型项目使用 xcconfig
相比把所有设置放在 project.pbxproj:
- Git Diff 更清晰。
- 合并冲突更少。
- 可以使用
#include复用。 - 环境差异更明确。
- 便于 CI 覆盖。
- 更容易审计。
13.3 推荐结构
text
Configurations/
├── Base.xcconfig
├── Debug.xcconfig
├── Staging.xcconfig
└── Release.xcconfig
Debug.xcconfig:
text
#include "Base.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG
API_BASE_URL = https:/$()/dev-api.example.com
Release.xcconfig:
text
#include "Base.xcconfig"
API_BASE_URL = https:/$()/api.example.com
https:/$()/ 是 xcconfig 中避免 // 被当作注释的一种写法。
13.4 不要在 xcconfig 中提交生产密钥
Build Setting 最终可能出现在:
- 构建日志。
- App Bundle。
- Process Environment。
- 反编译字符串。
客户端无法安全保存服务端 Secret。敏感凭证应由服务端保管,通过短时 Token 等机制下发。
13.5 当前项目情况
当前 WebRTC Demo 没有使用独立 .xcconfig,设置都保存在 project.pbxproj。
对于学习 Demo 可以接受,正式公司项目建议逐步迁移公共配置和环境差异。
14. Package Dependencies 是什么
14.1 Swift Package 的四个层次
需要区分:
text
Package
├── Products
│ ├── Library
│ └── Executable
└── Targets
├── Source Target
├── Test Target
└── Binary Target
术语解释:
| 概念 | 含义 |
|---|---|
| Package | 由 Package.swift 描述的分发单元 |
| Target | Package 内的编译单元 |
| Product | Package 对外提供、可被消费的产物 |
| Dependency | Package 或 Target 对其他单元的依赖 |
Swift Package Target 和 Xcode Project Target 是相似但不同层次的构建单元。
14.2 Xcode Project 如何添加 Package
通过:
text
File -> Add Package Dependencies
Xcode 会处理:
- 保存远端 Git URL。
- 保存版本规则。
- 解析
Package.swift。 - 选择 Package Product。
- 把 Product 添加到指定 App Target。
- 更新
Package.resolved。 - 在构建时准备源码或 Binary Artifact。
14.3 当前 Project 中的声明
WebRTC:
text
repositoryURL = https://github.com/stasel/WebRTC
minimumVersion = 150.0.0
kind = upToNextMajorVersion
允许范围:
text
150.0.0 <= version < 151.0.0
Starscream:
text
repositoryURL = https://github.com/daltoniam/Starscream.git
minimumVersion = 4.0.0
kind = upToNextMajorVersion
允许范围:
text
4.0.0 <= version < 5.0.0
14.4 Package Product 如何加入 Target
当前 WebRTC-Demo Target 有:
text
packageProductDependencies:
WebRTC
Starscream
Frameworks Build Phase 也包含:
text
WebRTC in Frameworks
Starscream in Frameworks
因此 App 源码可以:
swift
import WebRTC
import Starscream
只把 Package 加到 Project,而不把 Product 加给目标 Target,源码仍可能出现:
text
No such module
14.5 Source Package 和 Binary Package
Starscream 是源码 Package:
text
Xcode 获取源码
-> 编译 Starscream Target
-> 生成 Module/Product
-> 链接到 App
WebRTC 150.0.0 是 Binary Target:
text
Xcode 下载 WebRTC.xcframework
-> 校验 Checksum
-> 选择当前平台 Slice
-> 链接并嵌入 App
14.6 Package 源码为什么不在 App Sources 中
SwiftPM 通常把 checkout 和 Artifact 放到:
text
DerivedData/<Project>/SourcePackages/
Xcode 的 Package Dependencies 是依赖图展示,不代表文件已复制到项目的 Sources 目录。
15. Package.resolved 是什么
15.1 声明范围和实际 Pin
Project 声明:
text
Starscream 允许 4.0.0..<5.0.0
Workspace 的 Package.resolved 锁定:
text
Starscream 4.0.4
revision df8d820...
Project 声明回答"允许什么版本",锁文件回答"这次实际使用什么版本"。
15.2 当前 Pin
text
Starscream 4.0.4
WebRTC 150.0.0
团队应提交可信的 Package.resolved,保证本地和 CI 使用相同版本。
15.3 当前工程的锁文件问题
当前外层 Workspace 锁定 WebRTC 150.0.0,但 Project 内部隐式 Workspace 残留 WebRTC 125.0.0。
由于 Project 当前最低要求已经是 150.0.0,125.0.0 不满足版本规则。
团队应:
- 统一打开外层
.xcworkspace。 - 以外层 Workspace 锁文件为准。
- CI 使用同一个 Workspace。
- 不直接手改锁文件版本。
- 统一清理旧解析状态。
15.4 依赖升级不是只改数字
升级 Package 后应验证:
- 编译 API 兼容性。
- 最低 iOS 版本。
- Xcode/Swift 工具链要求。
- 真机和 Simulator Slice。
- 二进制签名与供应链。
- 行为回归。
- License。
- App 体积。
16. Info.plist、Entitlements 和 Capabilities
16.1 Info.plist
Info.plist 描述 Bundle 元数据和运行时声明,例如:
- Bundle Identifier。
- 版本号。
- 可执行文件名。
- 权限用途文案。
- URL Schemes。
- 后台模式。
- 支持方向。
当前 Info.plist 包括:
text
NSCameraUsageDescription
NSMicrophoneUsageDescription
UIBackgroundModes = voip
UILaunchStoryboardName
UIRequiredDeviceCapabilities = arm64
16.2 Entitlements
Entitlements 描述由签名授权的系统能力,例如:
- Push Notifications。
- Associated Domains。
- App Groups。
- Keychain Groups。
- iCloud。
- Sign in with Apple。
Target 通常通过:
text
CODE_SIGN_ENTITLEMENTS
指向 .entitlements 文件。
当前 Demo 没有独立 Entitlements 文件。
16.3 Capabilities
Xcode 的 Signing & Capabilities 页面可能同时修改:
- Entitlements。
- App ID 能力。
- Build Settings。
- Info.plist。
Capabilities 不是纯 UI 标签,它会影响签名和系统权限。
16.4 权限文案和 Entitlement 不同
相机权限:
text
Info.plist 权限用途文案
Push:
text
Entitlement + App ID Capability + Provisioning Profile
不能把两者混为一谈。
17. Code Signing 如何进入工程
17.1 签名解决什么问题
签名用于证明:
- Product 来自哪个开发团队。
- Bundle Identifier 是否被授权。
- Entitlements 是否被允许。
- App 是否被修改。
- 哪些设备或渠道可以安装。
17.2 当前签名配置
App Project 当前包含:
text
CODE_SIGN_STYLE = Automatic
DEVELOPMENT_TEAM = J5HXNWPVL8
PRODUCT_BUNDLE_IDENTIFIER = com.stasel.WebRTC
这是原作者配置。其他开发者真机运行时通常需要:
- 选择自己的 Team。
- 使用可用 Bundle Identifier。
- 让 Xcode 生成或选择 Provisioning Profile。
17.3 Simulator 与真机差异
Simulator:
- 不需要普通 iOS 真机 Provisioning Profile。
- 使用 Mac 上的模拟环境。
- CPU Slice 和系统能力与真机不同。
真机:
- 需要签名和设备授权。
- 使用
iphoneosSDK Product。 - 必须包含设备可执行架构。
- 部分硬件能力只能真机验证。
17.4 Archive 与普通 Run 差异
Run:
- 通常 Debug。
- 安装到当前 Destination。
- 附加 LLDB。
Archive:
- 通常 Release。
- 生成
.xcarchive。 - 包含 App、dSYM 和签名信息。
- 后续用于 TestFlight、App Store 或企业分发。
18. 从点击 Run 到 App 启动发生了什么
以当前 WebRTC-Demo Scheme 为例。
18.1 选择上下文
Xcode 确定:
text
Workspace: WebRTC-Demo.xcworkspace
Scheme: WebRTC-Demo
Action: Run
Configuration: Debug
Destination: 某台 iPhone 或 Simulator
18.2 构建依赖图
Xcode 读取:
- Scheme Build Action。
- App Target。
- Package Product Dependencies。
- Target Dependencies。
- Build Phases。
- Build Settings。
得到类似:
text
Starscream Product ─┐
├──> WebRTC-Demo Target
WebRTC Product ─────┘
SignalingServer 不在这张构建依赖图中。
18.3 解析 Package
Xcode:
- 读取 Package References。
- 读取
Package.resolved。 - 获取 Starscream 源码。
- 获取 WebRTC Binary Artifact。
- 准备对应平台和架构。
18.4 编译源码
Swift 编译器处理 Compile Sources,执行:
- 语法和类型检查。
- 条件编译。
- Module 生成。
- 对象文件生成。
Debug 下使用:
text
-Onone
DEBUG
ENABLE_TESTABILITY
18.5 编译资源
资源工具处理:
- Asset Catalog。
- XIB。
- Storyboard。
- Launch Screen。
18.6 链接
链接器组合:
- App 对象文件。
- Starscream。
- WebRTC。
- 系统 Framework。
生成 App 可执行文件。
18.7 组装 Bundle
构建系统:
- 处理 Info.plist。
- 复制编译后的资源。
- 嵌入所需 Framework。
- 设置 Runpath。
- 生成 App Bundle。
18.8 签名、安装和启动
真机时:
- Code Sign。
- 安装到设备。
- 启动 App。
- LLDB 附加进程。
运行到:
AppDelegate.swift 的 @UIApplicationMain 入口。
18.9 运行时才连接 Server
App 启动后,MainViewController 调用:
text
SignalingClient.connect()
这时才通过网络寻找 SignalingServer。
Server 不在线不会阻止 App 编译,但会导致运行时信令连接失败。
19. Build、Run、Test 和 Archive 的区别
19.1 Build
只构建 Product,不一定安装和启动。
适合:
- 验证编译。
- CI 快速检查。
- 构建 Framework。
19.2 Run
text
Build + Install + Launch + Debug Attach
可带启动参数和环境变量。
19.3 Test
构建:
- App/Test Host。
- Unit Test Bundle。
- UI Test Runner。
然后由 XCTest 执行 Scheme Test Action 中选中的 Testables。
当前 Demo Testables 为空,所以没有实际测试。
19.4 Profile
通常以 Release 配置启动 Product,并交给 Instruments:
- Time Profiler。
- Allocations。
- Leaks。
- Network。
- Energy。
19.5 Analyze
执行静态分析,查找:
- 潜在内存和资源问题。
- 不可达逻辑。
- Objective-C/C 相关缺陷。
它不能替代编译、测试和运行时验证。
19.6 Archive
生成归档 Product:
text
.xcarchive
├── Products/Applications/App.app
├── dSYMs/
└── Info.plist
Archive 成功也不代表上传一定成功,还要满足:
- 签名。
- Entitlements。
- App Store 验证。
- 隐私 Manifest。
- 架构。
- Bundle Version。
20. Scheme、Configuration 与环境如何组合
推荐将"执行入口"和"设置集合"分开理解:
text
Scheme
-> 为每个 Action 选择 Configuration
-> Configuration 提供 Build Settings
-> Build Settings 决定 Bundle ID、API Host、优化和签名
20.1 一个常见设计
text
Configurations:
Debug
Staging
Release
Schemes:
CompanyApp-Dev
CompanyApp-Staging
CompanyApp
组合:
| Scheme | Run | Archive | Bundle ID |
|---|---|---|---|
| CompanyApp-Dev | Debug | Debug | com.company.app.dev |
| CompanyApp-Staging | Staging | Staging | com.company.app.staging |
| CompanyApp | Release | Release | com.company.app |
20.2 环境差异不应散落在源码
不推荐:
swift
#if DEBUG
let api = "https://dev..."
#else
let api = "https://prod..."
#endif
少量编译条件可以接受,但大量环境逻辑会变得不可测试。
更清晰的方式:
- xcconfig 提供值。
- Info.plist 展开 Build Setting。
- App 启动时读取结构化配置。
- 服务端 Secret 不进入客户端。
21. 如何创建和使用 Target
21.1 新建 Unit Test Target
通过:
text
File -> New -> Target -> Unit Testing Bundle
Xcode通常会:
- 创建 Test Target。
- 创建
.xctestProduct。 - 添加测试源码。
- 配置 Test Host。
- 把 Test Target 加入 Scheme Test Action。
需要确认:
- Test Target 的 Host Application。
@testable import对应 Module。- Scheme 是否共享。
- CI 是否执行该 Scheme。
21.2 新建 App Extension Target
例如 Notification Service Extension:
- 新建 Extension Target。
- 使用唯一 Bundle Identifier。
- 配置 Deployment Target。
- 添加 Extension Info.plist。
- 配置 Entitlements。
- App Target 嵌入 Extension Product。
Extension 是独立进程和独立 Bundle,不是 App 中的普通类。
21.3 新建内部 Framework Target
适用于:
- 清晰模块边界。
- 多 Product 复用。
- 限制依赖方向。
需要同时配置:
- App 对 Framework 的 Target Dependency。
- Link Binary With Libraries。
- 必要的 Embed Frameworks。
- Framework 的 Public API。
不要只为目录整洁创建大量 Framework,模块化应解决真实边界和构建问题。
22. 如何添加和管理 Package Dependency
22.1 添加步骤
File -> Add Package Dependencies。- 输入 Git URL。
- 选择版本规则。
- 选择 Package Product。
- 选择消费该 Product 的 Target。
- 等待解析和下载。
- 提交 Project 变化与锁文件。
22.2 版本规则怎么选
常见规则:
| 规则 | 特点 | 建议 |
|---|---|---|
| Up to Next Major | 允许兼容版本升级 | 成熟 SemVer Package 常用 |
| Up to Next Minor | 更保守 | 兼容性不稳定时使用 |
| Exact Version | 完全锁定 | 高风险或特殊合规依赖 |
| Branch | 浮动 | 不推荐生产 |
| Revision | 固定 Commit | 临时修复或无法打 Tag 时 |
即使有范围,也应提交 Package.resolved。
22.3 移除 Package 的正确顺序
- 移除源码中的
import和 API 使用。 - 从 Target 移除 Package Product。
- 从 Frameworks Build Phase 确认移除。
- 从 Project 移除 Package Reference。
- 重新解析依赖。
- 清理无效锁定记录。
- 全量构建和测试。
只从 Navigator 删除 Package,可能留下 Target 引用或编译错误。
23. 多 Project Workspace 如何组合
23.1 只放在一起
当前 Demo:
text
Workspace
├── iOS App Project
└── macOS Server Project
二者只共享开发窗口,没有构建依赖。
23.2 跨 Project 编译依赖
另一个常见结构:
text
Workspace
├── App.xcodeproj
│ └── App Target
└── SDK.xcodeproj
└── SDKFramework Target
App 可以:
- 添加
SDKFrameworkProduct 引用。 - 建立 Target Dependency。
- 链接 Framework。
- 必要时嵌入 Framework。
Xcode 在同一 Workspace 中可以解析跨 Project 的 Target。
23.3 使用 Local Swift Package
现代模块化也常使用:
text
Workspace/
├── App.xcodeproj
└── Packages/
├── Core/
│ └── Package.swift
└── InterviewFeature/
└── Package.swift
Local Package 优点:
- Manifest 是文本。
- 模块边界清晰。
- 可独立测试。
- 减少
project.pbxproj文件引用冲突。 - 支持跨平台。
限制:
- 资源和 Bundle 查找方式不同。
- 一些 Apple Target 类型仍需要 Xcode Project。
- Package Build Settings 自定义能力和 Xcode Target 不完全相同。
24. 正式 iOS 项目的推荐构成
一个中型 App 可以组织为:
text
CompanyApp.xcworkspace
│
├── App/
│ └── CompanyApp.xcodeproj
│ ├── CompanyApp Target
│ ├── CompanyAppTests Target
│ ├── CompanyAppUITests Target
│ └── Extensions
│
├── Packages/
│ ├── Core
│ ├── Networking
│ ├── DesignSystem
│ ├── Authentication
│ └── InterviewFeature
│
├── Configurations/
│ ├── Base.xcconfig
│ ├── Debug.xcconfig
│ ├── Staging.xcconfig
│ └── Release.xcconfig
│
└── Scripts/
依赖方向示例:
text
CompanyApp
-> InterviewFeature
-> Authentication
-> DesignSystem
-> Networking
-> Core
规则:
- Feature 不反向依赖 App。
- Core 不依赖具体业务 Feature。
- 模块间通过明确 Public API 通信。
- 避免形成循环依赖。
- 测试尽量靠近所属模块。
25. 当前 WebRTC Demo 的完整工程图
25.1 开发容器
text
WebRTC-Demo.xcworkspace
│
├── WebRTC-Demo.xcodeproj
│ ├── Project Debug/Release
│ ├── Target: WebRTC-Demo
│ │ ├── Target Debug/Release
│ │ ├── Compile Sources
│ │ ├── Link WebRTC
│ │ ├── Link Starscream
│ │ ├── Copy Resources
│ │ └── Product: WebRTC-Demo.app
│ ├── Shared Scheme: WebRTC-Demo
│ └── Remote Package References
│
└── SignalingServer.xcodeproj
├── Project Debug/Release
├── Target: SignalingServer
│ ├── Compile Sources
│ └── Product: macOS executable
└── Server Scheme
25.2 App 构建图
text
Starscream 4.0.4 Source Product ─┐
├── WebRTC-Demo.app
WebRTC 150.0.0 Binary Product ──┘
25.3 App 运行图
text
WebRTC-Demo.app
├── NativeWebSocket / Starscream
├── SignalingClient
└── WebRTCClient
│
│ ws://Mac-IP:8080
▼
SignalingServer
25.4 媒体建连后的关系
text
iOS A <========== WebRTC 音视频/DataChannel ==========> iOS B
SignalingServer 只交换 SDP/ICE,不转发媒体。
26. 当前工程的工程化观察
26.1 做得清晰的部分
- App 和 Server 是独立 Target。
- Workspace 明确组合两个 Project。
- WebSocket 实现通过协议抽象。
- Package Product 正确挂到 App Target。
- App Scheme 已共享。
- Debug 和 Release 配置分离。
- 源码与资源 Build Phase 清晰。
26.2 需要注意的问题
Config.swift仍有 Xcode URL 占位符。- 没有 Unit Test 或 UI Test Target。
- 两份
Package.resolved的 WebRTC Pin 不一致。 - App Deployment Target 为 iOS 12。
- 工程设置仍集中在
project.pbxproj。 - 使用原作者 Team 和 Bundle Identifier。
- 没有 Staging/Production 环境模型。
- SignalingServer 只是无房间、无鉴权的广播服务。
UIBackgroundModes = voip不应直接用于正式面试 App。
26.3 不应为了学习而一次性重构
当前 Demo 的价值是理解:
- WebRTC API。
- SDP/ICE。
- WebSocket 信令。
- Xcode 多 Project Workspace。
- SwiftPM Package Product。
正式业务应在公司主工程中按现有架构规范接入 LiveKit,而不是把这个 Demo 直接扩展成生产项目。
27. 常见误解与纠正
27.1 Project 就是 App
不准确。
Project 可以包含多个 Target,只有 Application Target 才生成 App。
27.2 Target 就是 Scheme
错误。
Target 是构建配方;Scheme 是如何 Build、Run、Test、Archive Target 的 执行配方。
27.3 文件在 Xcode 中就会被编译
错误。
需要检查 Target Membership 和 Compile Sources。
27.4 Workspace 中的所有 Project 会一起构建
错误。
Scheme 和依赖图决定构建哪些 Target。
27.5 Package 出现在列表中就能 import
不一定。
还需要把正确 Package Product 添加给消费它的 Target。
27.6 Debug 就是测试服务器
错误。
Debug 是编译配置。后端环境需要显式配置。
27.7 Release 一定可上传 App Store
错误。
Release 只是一组设置。上传还依赖 Archive、签名、Entitlements、版本、 隐私和 App Store 校验。
27.8 Build 成功说明服务器连接正常
错误。
服务器属于运行时依赖,Build 不会验证网络服务。
27.9 删除 DerivedData 会修改源码
正常情况下不会。
DerivedData 是中间产物和缓存;删除后会增加下一次构建时间。
27.10 Package.resolved 可以随便不提交
对 App 工程通常不建议。
不提交会让不同机器和时间解析出不同依赖版本,增加不可复现风险。
28. 接手陌生 iOS 工程的阅读顺序
28.1 第一步:找正确入口
优先查找:
text
*.xcworkspace
*.xcodeproj
README
Podfile
Package.swift
Package.resolved
如果同时存在 .xcworkspace 和 .xcodeproj,先阅读 README,再判断是否应打开 Workspace。
28.2 第二步:列出 Project 和 Target
确认:
- 有几个 Project。
- 每个 Project 有哪些 Target。
- 每个 Target 的 Product Type。
- 哪些是 App、Tests、Extensions、Framework。
28.3 第三步:查看 Scheme
确认:
- 团队共享哪些 Scheme。
- Run 使用什么 Configuration。
- Archive 使用什么 Configuration。
- Test Action 是否包含测试 Target。
- CI 使用哪个 Scheme。
28.4 第四步:查看依赖图
确认:
- Target Dependencies。
- Package Dependencies。
- CocoaPods/Carthage。
- Link Binary With Libraries。
- Embed Frameworks。
- 运行时服务。
28.5 第五步:查看 Build Settings
重点:
text
PRODUCT_BUNDLE_IDENTIFIER
DEVELOPMENT_TEAM
IPHONEOS_DEPLOYMENT_TARGET
SWIFT_VERSION
INFOPLIST_FILE
CODE_SIGN_ENTITLEMENTS
SWIFT_ACTIVE_COMPILATION_CONDITIONS
OTHER_SWIFT_FLAGS
OTHER_LDFLAGS
28.6 第六步:查看 Build Phases
重点排查:
- 源码是否属于正确 Target。
- 资源是否重复。
- Framework 是否正确链接和嵌入。
- Run Script 是否稳定。
- 脚本是否泄漏密钥。
28.7 第七步:找运行入口
UIKit:
text
@UIApplicationMain
@main AppDelegate
SceneDelegate
SwiftUI:
swift
@main
struct CompanyApp: App
28.8 第八步:区分构建问题和运行问题
构建问题:
- Package 无法解析。
- No such module。
- Linker Error。
- Signing Error。
运行问题:
- API 地址错误。
- 服务器不可达。
- 权限被拒绝。
- 数据解析失败。
- WebRTC 连接失败。
29. 常用命令行工具
29.1 查看 Workspace 的 Scheme
bash
xcodebuild -list \
-workspace WebRTC-Demo-App/WebRTC-Demo.xcworkspace
29.2 查看 Effective Build Settings
bash
xcodebuild -showBuildSettings \
-workspace WebRTC-Demo-App/WebRTC-Demo.xcworkspace \
-scheme WebRTC-Demo \
-configuration Debug
29.3 解析 Package
bash
xcodebuild -resolvePackageDependencies \
-workspace WebRTC-Demo-App/WebRTC-Demo.xcworkspace \
-scheme WebRTC-Demo
29.4 构建 Simulator
bash
xcodebuild build \
-workspace WebRTC-Demo-App/WebRTC-Demo.xcworkspace \
-scheme WebRTC-Demo \
-configuration Debug \
-destination 'generic/platform=iOS Simulator'
29.5 构建真机通用 Product
bash
xcodebuild build \
-workspace WebRTC-Demo-App/WebRTC-Demo.xcworkspace \
-scheme WebRTC-Demo \
-configuration Debug \
-destination 'generic/platform=iOS'
真机构建仍需要正确签名。
29.6 CI 中的重要原则
- 明确 Workspace。
- 明确 Scheme。
- 明确 Configuration。
- 明确 Destination。
- 使用固定 Xcode 版本。
- 提交 Package 锁文件。
- 缓存可再生依赖,但不能依赖脏缓存。
- 保存
.xcresult和 dSYM。
30. Git 中应该提交什么
通常提交:
text
*.xcodeproj/project.pbxproj
*.xcworkspace/contents.xcworkspacedata
xcshareddata/xcschemes/*.xcscheme
Package.resolved
*.xcconfig
Info.plist
*.entitlements
源码和资源
通常忽略:
text
xcuserdata/
DerivedData/
*.xcuserstate
*.moved-aside
.DS_Store
是否提交用户 Scheme、Package 缓存或生成文件,应由团队规范统一,不要由开发者 各自决定。
31. pbxproj 合并冲突如何处理
31.1 为什么容易冲突
多人同时执行以下操作会修改同一对象图:
- 添加文件。
- 新建 Group。
- 新建 Target。
- 添加 Package。
- 修改 Build Settings。
- 修改 Build Phases。
31.2 处理原则
- 不直接选择 ours/theirs 覆盖整个文件。
- 理解双方分别增加了哪些对象。
- 保留 File Reference、Build File 和 Build Phase 三者关系。
- 检查 UUID 是否重复或丢失。
- 用 Xcode 打开工程验证。
- 检查 Target Membership。
- 执行干净构建和测试。
31.3 降低冲突的方法
- 小步提交工程文件修改。
- 功能源码优先放 Local Swift Package。
- 使用 xcconfig 减少 Build Settings 变更。
- 不提交
xcuserdata。 - 避免多人同时大规模移动 Group。
- 使用稳定的工程生成工具时,团队必须统一流程。
32. 学习这些概念的实践练习
32.1 练习一:观察 Target Membership
- 新建
DemoLogger.swift。 - 取消 App Target Membership。
- 在 App 中引用类型,观察编译错误。
- 重新勾选 Membership。
- 在 Compile Sources 中确认记录。
32.2 练习二:创建测试 Target
- 新建 Unit Test Target。
- 查看新增 Product。
- 查看 Scheme Testables。
- 查看 Build Settings 中的 Test Host。
- 执行
Command-U。
32.3 练习三:复制 Configuration
- 将 Debug 复制为 Staging。
- 创建 Staging Scheme。
- 为 Staging 设置不同 Bundle ID。
- 通过 Info.plist 展开环境名称。
- 查看 Products 是否可以共存安装。
32.4 练习四:添加一个 Local Package
- 创建
CoreKitPackage。 - 添加 Library Product。
- App Target 引用 Product。
- 在 App 中
import CoreKit。 - 查看依赖图和 DerivedData。
32.5 练习五:理解 Workspace
- 单独打开
.xcodeproj。 - 观察 SignalingServer 是否可见。
- 关闭后打开外层
.xcworkspace。 - 观察两个 Project 和 Scheme。
- 对比 Package 锁文件范围。
练习应在临时分支或副本中完成,避免污染当前学习工程。
33. 核心概念速查表
| 概念 | 一句话定义 | 当前项目实例 |
|---|---|---|
| Workspace | 多 Project 工作容器 | WebRTC-Demo.xcworkspace |
| Project | 保存 Target 和构建配置 | WebRTC-Demo.xcodeproj |
| Target | 输入到 Product 的构建配方 | WebRTC-Demo |
| Product | Target 构建输出 | WebRTC-Demo.app |
| Scheme | Build/Run/Test/Archive 执行配方 | WebRTC-Demo.xcscheme |
| Configuration | 有名字的 Build Settings 集合 | Debug、Release |
| Build Settings | 编译、链接、签名参数 | Bundle ID、Swift Version |
| Build Phases | Target 的处理步骤 | Sources、Frameworks、Resources |
| Target Membership | 文件参与哪些 Target | WebRTCClient.swift 属于 App |
| Target Dependency | Target 间构建顺序和依赖 | 当前 App 与 Server 没有 |
| Package | SwiftPM 分发单元 | Starscream、WebRTC |
| Package Product | Package 对外提供的产物 | Starscream Library、WebRTC Binary |
| Package.resolved | 当前解析版本 Pin | 4.0.4、150.0.0 |
| Info.plist | Bundle 元数据和权限声明 | 相机、麦克风权限 |
| Entitlements | 签名授权的系统能力 | 当前 Demo 无独立文件 |
| DerivedData | 构建产物与缓存 | Build、SourcePackages |
34. 最终总结
理解 iOS 工程时,可以始终按下面的顺序推导:
text
我打开哪个 Workspace?
-> Workspace 中有哪些 Project?
-> 每个 Project 中有哪些 Target?
-> Target 生成什么 Product?
-> Scheme 要构建和运行哪些 Target?
-> Action 使用哪个 Configuration?
-> 最终 Build Settings 是什么?
-> Target 执行哪些 Build Phases?
-> 依赖哪些 Target、Framework 和 Package Product?
-> 最终 Product 如何签名、安装和运行?
-> Product 运行时还依赖哪些后端服务?
在当前 WebRTC Demo 中:
WebRTC-Demo.xcworkspace是总开发入口。- Workspace 引用 iOS App 和 macOS SignalingServer 两个 Project。
- 两个 Project 各有一个 Target,并生成不同平台的 Product。
WebRTC-DemoScheme 负责 App 的 Run、Profile 和 Archive。- App Target 的 Debug/Release Configuration 决定编译和签名参数。
- Build Phases 决定哪些源码、资源和 Framework 进入 App。
- Starscream 与 WebRTC 通过 SwiftPM Product 链接到 App Target。
- SignalingServer 不属于 App 构建依赖,而是运行时 WebSocket 服务。
只要把"开发容器、构建单元、执行配方、设置集合、依赖和运行时服务"分层, 复杂 iOS 项目的结构就可以被逐步还原,而不再只是 Xcode 左侧的一棵文件树。
35. 关键文件索引
- 外层 Workspace 配置
- App Project 配置
- App Shared Scheme
- Workspace Package.resolved
- App Info.plist
- App 入口
- SignalingServer Project
- SignalingServer 入口
- 项目 README
36. Apple 官方参考
- Xcode Build System
- Xcode Concepts: The Workspace Window
- Customizing the Build Schemes for a Project
- Adding Package Dependencies to Your App
- Build Settings Reference
- Configuring the Build Settings of a Target
- Running Your App in Simulator or on a Device
- Distributing Your App for Beta Testing and Releases