Swift项目生成Framework流程以及与OC的区别
一. 引言
Swift 已经成为 iOS 开发的主流语言,在实际项目中,打包成 Framework(二进制框架) 已经是模块化、组件化开发的常见需求。
对于 Objective-C 而言,制作 Framework 几乎是轻车熟路的流程:只需设置好暴露头文件,一切都能顺利调用。
但当我们用 Swift 来生成 Framework 时,对于新手而言往往会遇到各种让人困惑的问题——
比如:
- 源码似乎被“暴露”出去了?
 - 明明设置了暴露文件却依旧无法访问?
 - public、internal 的边界到底是如何决定的?
 
在本篇文章中,我们将以一个“播放器”为例,从零开始讲解 Swift 项目生成 Framework 的完整流程,并重点分析它与 Objective-C 在模块暴露机制、访问控制以及编译产物结构上的区别。希望读完之后,你能彻底搞懂 Swift Framework 打包背后的逻辑。
二. 生成步骤
接下来我们就以一个“播放器”为例,演示如何从零开始创建并打包一个 Swift Framework。
在这个示例中,我们将创建一个名为 PlayerFramework 的框架,用来模拟一个简单的播放器逻辑。
其中会包含:
- 两个公开方法:play() 和 pause() —— 对外提供播放与暂停功能。
 - 一个私有方法:loadMediaFile()加载资源,仅供框架内部使用。
 
完成后,我们会将生成的 .framework 文件导入到另一个示例项目中进行测试,确保外部调用正常、私有方法不可访问。
2.1 创建Framework工程
对于创建工程的步骤OC项目和Swift项目到的确是没有什么区别。
新建一个Project,菜单栏选择“iOS” ,向下拉到“Framework & Library”然后选择Framework。

然后输入 “Product Name” 选择 “Team”等等信息,Most important of all ,“Language”要选择Swift,不然就该去看OC教程了。

创建好的工程目录结构如下:

1. 最上面的 PlayerFramework 工程的根目录。
2. PlayerFramework 文件夹 是源码文件,这个目录才是 真正的代码与配置文件所在位置,一般包括:
- PlayerFramework.h
 - Swift 源文件(比如稍后你要放的 PlayerHelper.swift)
 - Info.plist(框架的配置文件)
 
PlayerFramework.h:
这个是 Umbrella Header(总头文件),在 Objective-C 框架中非常重要,用于声明哪些符号对外暴露。
但在 纯 Swift Framework 中,这个文件基本不会起作用,
因为 Swift 的符号暴露完全由关键字 public / internal / private 控制。
3. PlayerFramework( 图书馆图标)
这是 编译产物文件夹(Build Products),也就是编译完成后生成的真正的 .framework 文件。
每次你在 Xcode 里 Build 后,系统会在这里生成一份新的 Framework。
它一般包含:
- 一个 “📖 图书图标” 的文件:这是实际的二进制可执行文件(PlayerFramework 本体),内部就是 Swift 编译器输出的 .o 链接产物。
 - 一个 Resources/ 文件夹:存放资源文件(图片、plist、json等),如果你的框架带有资源就会放在这里。
 
这个文件夹并不是你自己维护的,而是 Xcode 自动生成的 编译产物。
2.2 构建PlayeHelper类
接下来,我们来创建一个示例类 —— PlayerHelper,用来模拟播放器的核心逻辑。在这个例子中,我们会定义两个对外暴露的公共方法:play() 和 pause(),同时在内部实现一个私有的加载方法 loadMediaFile()。
文件位置
该文件应该在“PlayerFramework”文件夹下与 “PlayerFramework.h”同级。

PlayerHelper.swift 示例代码
代码内加了完整的注释,需要注意的是整个类需要使用public修饰,对外暴露的方法也需要使用public修饰。
import Foundation/// 播放器辅助类
/// 用于提供外部调用的播放与暂停接口。
public class PlayerHelper {/// 初始化方法public init() {}// MARK: - 对外暴露的接口/// 开始播放/// 调用该方法后会触发内部加载逻辑(示意)public func play() {print("🎵 Start playing...")loadMediaFile()}/// 暂停播放/// 暂停当前的播放任务public func pause() {print("⏸ Pause playback.")}// MARK: - 私有逻辑(不会对外暴露)/// 加载媒体文件,仅供内部调用/// 外部工程无法访问此方法private func loadMediaFile() {print("🔒 Load media from local file system.")}
} 
 
2.3 设置配置信息
接下来我们还需要到“Build Setting”中查看一下配置信息,大多数情况下这些配置都是符合要求的,但是我们在这里还是来介绍一下,以便满足不同的需求。
设置 Defines Module
Build Settings → Packaging → Defines Module
 将其设置为YES。
  
- 表示当前 Framework 会作为一个独立的 Swift Module 存在。
 - 外部项目才能通过 import PlayerFramework 导入你的框架。
 - 如果关闭,则 Swift 编译器无法识别模块名。
 

设置 Build Libraries for Distribution
Build Settings → Build Options → Build Libraries for Distribution
 将其值设置为:YES。
- 启用 Module Stability(模块稳定性),可避免因 Swift 版本不同而无法加载。
 - 生成 .swiftinterface 文件,用于暴露框架 API 接口签名(不会泄露实现代码)。
 - 这是 Swift 框架“被安全调用”的关键一步。
 

确认 Mach-O Type
Build Settings → Linking → Mach-O Type
选择动态库还是静态库。这个根据自己需要来选择,希望简单点的话直接选择静态库“ Static Library”。
- 表示生成的 Framework 是一个可动态加载的库。
 - 这也是我们常说的 “动态 Framework”,可以被多个 App 引用而不重复加载。
 - 若选择 Static Library,则会在编译时被打包进 App,这种方式适用于 SDK,不适合模块化分发。
 

2.4 编译生成Framework
配置信息都检查完成之后,我们直接运行项目或者“command+B” 编译项目,关于生成的是真机使用的还是模拟器:
在Xcode的左上角选择目标设备,可以选择“Any iOS Device (arm64)”来编译适用于真机的版本,或者“Any iOS Simulator Device(arm64,x86_64)”。
也可以参考下面的文章进行合并:
https://blog.csdn.net/weixin_39339407/article/details/142988880?fromshare=blogdetail&sharetype=blogdetail&sharerId=142988880&sharerefer=PC&sharesource=weixin_39339407&sharefrom=from_link
编译成功之后之后,在菜单栏选择“Project” -> “Show build Folder in Finder”

就可以看见我们已经生成的Framework:

我们编译和运行的都是debug包,不能在生产环境使用嗷。
 生成release环境的Framework可以选择“Archive”,如果有不太明白的可以私信或者留言。
三. Swift生成的Framework内容解析
当我们构建完成后(无论是运行在真机还是模拟器),在 Products 文件夹中会生成一个成品:

3.1 Headers 文件夹
PlayerFramework.framework/Headers/PlayerFramework.h
这个文件就是我们在工程里看到的 Umbrella Header。
在 Objective-C 框架中,这个文件非常重要,
因为只有被列入公共头文件(Public Headers)的符号,才能被外部访问。
但在 Swift 框架中情况完全不同: Swift 框架不会依赖 .h 文件
- 所有对外可见的符号由 public / open 修饰符控制;
 - 即使删除 .h 文件,Swift 框架依然可以被正常导入;
 - .h 文件的存在主要是为了 兼容 Objective-C 调用 Swift 框架 的情况。
 
所以,如果你纯 Swift 开发,这个文件几乎没用,只是个“占位符”。
3.2 Info.plist
这是 Framework 的元信息文件,包含:
- 框架的版本号
 - 编译环境信息
 - Bundle Identifier 等
 
你基本不需要手动修改它。
3.3 Modules 文件夹
这个文件夹是 Swift 框架能被识别为 模块(Module) 的关键。
module.modulemap
这个文件告诉编译器:“这个 Framework 是一个模块,模块名叫 PlayerFramework。”
内容通常如下:
framework module PlayerFramework {umbrella header "PlayerFramework.h"export *module * { export * }
} 
这段配置表示:
- 框架的对外入口是 PlayerFramework.h
 - 模块名为 PlayerFramework
 - 允许 Swift 和 Objective-C 互操作(export *)
 
即使你没有手动设置,Xcode 会自动生成。
PlayerFramework.swiftmodule
这个文件夹里放的是 Swift 模块描述文件,它包含几种文件类型:
|   文件  |   作用  | 
|---|---|
|   .swiftmodule  |   Swift 编译器读取的模块定义(二进制格式)  | 
|   .swiftdoc  |   Swift 文档元数据(方法签名、注释等)  | 
|   .swiftinterface  |   当你启用 “Build Libraries for Distribution = YES” 后生成的 文本描述文件,用于模块稳定性  | 
3.4 PlayerFramework (二进制文件)
这是整个框架的核心部分。它是编译后的 Mach-O 动态库(.dylib 格式),包含你所有 Swift 源码的编译产物。
四. 使用Framework
接下来我们新建一个普通的Swift项目就叫“TestFrameworkDemo”,然后呢将我们刚刚生成的PlayerFramework 导入到项目中。

在ViewController可以直接引用该Framework,然后按住“command”使用鼠标点击“PlayerFramework”进入内部会发现,只有我们设置为public的方法可以被看见,并且只有方法方法名,并不会暴露方法内部的实现:

里面所有使用public修饰的方法,在外面都可以直接被调用。
五. Swift 与 Objective-C 的区别
其实OC与Swift构建Framework会有区别的根本原因在于Swift 与 Objective-C 在框架暴露机制上的本质区别。
5.1 Objective-C:通过「头文件」决定暴露范围
在 Objective-C 的世界里,所有的符号暴露都是通过 头文件(.h) 来完成的。
如果你希望外部项目能访问某个类或方法,必须:
- 把对应的 .h 文件添加到
 - Build Phases → Headers → Public 下;
 - 在 Umbrella Header(例如 PlayerFramework.h)中 #import 进来。
 
// PlayerManager.h
@interface PlayerManager : NSObject
- (void)play;
@end 
若没有将 PlayerManager.h 添加为 Public,
即使你在别的文件中 import 了,也无法被外部工程识别。
所以,在 OC 框架中,“文件是否被暴露”完全取决于你有没有公开头文件。
5.2 Swift:通过「访问控制修饰符」决定暴露范围
Swift 的设计理念是模块化和安全性优先,
因此框架的暴露范围不再依赖外部文件配置,而是完全由 访问控制修饰符 决定:
|   修饰符  |   可见范围  |   说明  | 
|---|---|---|
|   private  |   当前文件内  |   完全隐藏  | 
|   fileprivate  |   当前源文件  |   类似私有  | 
|   internal(默认)  |   当前模块  |   默认,仅框架内部可见  | 
|   public  |   可被其他模块访问  |   框架对外公开的 API  | 
|   open  |   可被访问且可被继承/重写  |   用于需要被扩展的 API  | 
无需再去设置 “Public Headers”,Swift 编译器会根据修饰符自动生成 .swiftinterface 接口文件,供外部模块识别和调用。
5.3 模块层面的区别
|   对比项  |   Objective-C  |   Swift  | 
|---|---|---|
|   暴露机制  |   头文件(Public Headers)  |   访问修饰符(public/open)  | 
|   入口文件  |   Umbrella Header (.h)  |   自动生成 modulemap  | 
|   接口文件  |   .h 文件  |   .swiftinterface 文件  | 
|   源码泄露风险  |   可能(若暴露实现文件)  |   无(只生成签名信息)  | 
|   模块导入方式  |   #import <Framework/Header.h>  |   import FrameworkName  | 
六.结语
通过本篇教程,我们完整演示了 Swift 项目生成 Framework 的流程,并重点解析了其与 Objective-C 的区别:
- Swift 不依赖 Public Headers 来控制暴露范围,而是通过 访问修饰符 决定哪些方法可被外部访问;
 - 编译生成的 .framework 中只包含 二进制和接口签名,源码不会暴露;
 - 对外暴露的 public 方法只是签名,对实现逻辑是不可见的,这让框架更加安全和可维护。
 
掌握这些原理后,你就可以自信地构建 Swift 框架,安全、规范地对外提供接口,同时保留内部实现的私密性。
