深入理解代替单纯记忆
问题背景
- 在开发iOS项目时,希望将一堆图片资源放入
Main Bundle中,但又不希望资源在Bundle的最顶层目录中,希望自定义目录 - 但一时想不到该如何解决,于是想到
Folder、Group等概念 - 经过简单搜索后,发现Xcode对于这两个概念的定义还是有些差异的
- 于是继续查阅学习了一番,编写本文,方便后续查阅和分享
本文提到的内容,参考的Xcode版本为26.0(17A324)和26.3(17C529)
Buildable Folder
Buildable Folder是自Xcode 16(2024年6月)引入的概念,初衷是为了减少代码管理中的冲突问题- 后续新建的工程或者新建
Folder时,默认都是Buildable Folder
官方原文如下:
Minimize project file changes and avoid conflicts with buildable folder references. Convert an existing group to a buildable folder with the Convert to Folder context menu item in the Project Navigator. Buildable folders only record the folder path into the project file without enumerating the contained files, minimizing diffs to the project when your team adds or removes files, and avoiding source control conflicts. To use a folder as an opaque copiable resource, the default behavior before Xcode 16, uncheck the Build Folder Contents option in the File Inspector.
Buildable Folder如何降低代码冲突
-
先添加1个普通Group--BuildableFolderTest,project文件的变化如下所示:
-
然后向BuildableFolderTest Group中添加
ABC.swift文件后,project文件的变化如下:这说明Group目录下的文件,都要在project文件中进行记录
-
继续,将BuildableFolderTest Group通过
Convert to Folder选项转为Folder(Buildable Folder)后,project文件的变化如下: -
然后再向BuildableFolderTest这个
Folder中添加DEF.swift文件后,发现project文件没有任何变化
所以,project文件仅记录了Folder自身,至于目录中的文件是不会记录在project文件中,所以会减少因团队多人同时修改Project文件导致的代码冲突
Apply to Each File vs Apply Once to Folder
当创建Folder(Buildable Folder)后,选中Folder,在File inspector中会看到有个Build Rule,有两个选择:Apply to Each File和Apply Once to Folder,默认是Apply to Each File
Apply Once to Folder
Apply Once to Folder开启后,project文件是什么样?
当开启该模式时,通过查看目录下的每个文件可以看出,文件是没有Target归属的概念的。同样,在该目录下创建新文件也不需要选择Target
再配合Xcode Buildable Folders中所提到的To use a folder as an opaque copiable resource, the default behavior before Xcode 16, uncheck the Build Folder Contents option in the File Inspector.
其实,Apply Once to Folder就是Xcode 16之前的Folder,之前叫Folder Reference
(在Xcode 16之前,创建Folder时,官方名称就叫做Folder Reference)
Folder Reference一般是用作资源包,目录下不包含源代码- 另一个
Folder Reference重要作用是可以在Bundle中自定义目录
Buildable Folder vs Folder Reference
Buildable Folder顾名思义,其中的内容是由编译系统参与的
- 所以
Buildable Folder中可以放源代码文件,并可以参与编译,打包到最终可执行文件中;也可以制定源文件的Target Folder Reference则保留老的逻辑,不参与编译,用作资源包,即使放入源代码文件也无法选择Target,只能当做普通文件资源处理
Create Group with Folder
同样是在Xcode 16开始的另一个变化是,创建Group时由原来的不自动创建磁盘物理目录(Folder)变为自动创建。当然,仍可以创建没有Folder的Group,原文如下:
Create groups with associated folders by default when using the New Group and New Group from Selection commands in the Project Navigator. To create a group without a folder, hold the Option key in the context menu to reveal the New Group without Folder variant of the command.
[Group without Folder] vs [Group] vs [Folder(Buildable Folder)] vs [Folder Reference]
| 特性 | Group without Folder | Group | Buildable Folder | Folder Reference |
|---|---|---|---|---|
| Project Navigator 图标 | ||||
| 是否对应磁盘目录 | ❌ 不必须 | ✅ 必须 | ✅ 必须 | ✅ 必须 |
| 工程结构是否可与磁盘不同 | ✅ 可以 | ❌ 基本一致 | ❌ 必须一致 | ❌ 必须一致 |
.pbxproj 是否记录每个文件 | ✅ 会 | ✅ 会 | ❌ 不会 | ❌ 不会 |
新增文件是否修改 .pbxproj | ✅ 会 | ✅ 会 | ❌ 不会 | ❌ 不会 |
| Git 冲突概率 | 高 | 高 | 低 | 低 |
| 是否参与编译系统 | ✅ | ✅ | ✅ | ❌ |
| 是否自动编译源码 | ✅ | ✅ | ✅(自动发现目录中的源码) | ❌ |
| Bundle 中是否保留目录结构 | ❌ 通常不会 | ❌ 通常不会 | ❌ 通常不会 | 会保留(如果被加入 Bundle) |
| 默认是否进入 Bundle | ❌ 否 | ❌ 否 | ❌ 否 | 仅在选中 Target 时自动加入 |
| 典型用途 | 逻辑分组 | 常规项目结构 | 源码目录 | 资源目录 |
- 当前(Xcode 26),默认的Group和Folder组合是
Group with Folder+Buildable Folder。这可能也意味着这两项是日常最常用的
回答开始的问题
- 既然是想打包资源放入
Bundle,并自定义目录,那必然是Folder Reference