XCFramework 是 Apple 提供的一种新的框架封装格式,从 2019 开始发布,XCFramework 以后应该能作为统一的二进制框架格式,用来替代现有的静态库与动态库格式。这个系列文章我们将来探讨各类框架的制作与使用场景。
本篇文章先看看 Swift 静态库的制作与使用。
现有的 Swift 静态库最终产物是 .a
结尾的文件和头文件,XCFramework 静态库在 .a
文件基础上进行进一步的封装,但本质没有变,内部还是 .a
文件。下面看看从头创建 XCFramework Swift 静态库的过程。
创建静态库工程
- 创建方式还和以前一样,选择 Static Library:
- 创建完成后,为了方便获取导出给 Objective-C 使用的头文件,在工程中创建一个 include 文件夹,把 Xcode 编译时自动生成的头文件放在此处:
- 编辑构建脚本,从 DerivedData 目录下把头文件取出来,复制头文件到 include 文件夹,脚本内容如下,第一次编译后把头文件(如
SwiftStaticLibraryExample-Swift.h
) 在 Xcode 中加入到 include 目录下,后续再编译即可自动更新:
# Type a script or drag a script file from your workspace to insert its path.
cp ${DERIVED_FILES_DIR}/${TARGET_NAME}-Swift.h ${SRCROOT}/include/
- 编写一个简单的类用于测试:
// SwiftStaticLibraryExample.swift
import Foundation
@objc public class SwiftStaticLibraryExample: NSObject {
@objc public var title = "Swift Static library is working."
}
生成 XCFramework 文件
- 编译后可看到 Products 目录下生成的
.a
文件,在 Finder 中打开这个文件复制到 XCFramework 的制作目录,目录中还有一个.swiftmodule
文件,这个给 Swift 代码调用时需要用到,现在暂时不用:
- 当前示例的 XCFramework 制作时只需要放入
.a
文件即可,在编译时分别针对模拟器和 iOS 设备分别运行一次,将两个.a
文件同时放入文件夹中,这样制作的 XCFramework 可同时运行于模拟器与真机:
- 在终端中进入
Swift Static XCFramework
文件夹,执行命令,将两个.a
文件都列出来:
xcodebuild -create-xcframework -library device/libSwiftStaticLibraryExample.a -library simulator/libSwiftStaticLibraryExample.a -output ./SwiftStaticLibraryExample.xcframework
- 创建后的 XCFramework 目录结构如下,可以看到内部按照不同的平台与架构分别创建了文件夹,Xcode 在运行时会自动选择对应的文件执行:
在 Objective-C 工程中使用 XCFramework 静态库
- 先创建一个默认的 Objective-C iOS 工程,在工程目录中新增一个 lib 目录,将前面生成的 XCFramework 文件拖拽到 Xcode 中:
- 在工程中添加静态库链接:
- 在工程中创建一个空的 Swift 文件,否则编译时将报错:
- 确认 Build Settings 中 Swift 版本为兼容版本:
- 在 TARGETS 的 Build Settings 中设置 Header Search Paths 为
${SRCROOT}/lib
,并选择 recursive. - 在 ObjC 工程中引入动态库头文件并调用测试类,运行后可看到可成功调用:
#import "ViewController.h"
#import "SwiftStaticLibraryExample-Swift.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *title = [SwiftStaticLibraryExample new].title;
NSLog(@"%@", title);
}
@end
// Swift Static library is working.
在 Swift 工程中使用 XCFramework 静态库
- 先创建一个默认的 Swift iOS 工程,在工程目录中新增一个 lib 目录,将前面生成的 XCFramework 文件拖拽到 Xcode 中,同时将静态库编译时生成的
.swiftmodule
文件也放入 lib 目录中 ,注意.swiftmodule
文件也要同时生成真机版与模拟器版,将两份文件合并到一个.swiftmodule
文件中即可,并注意添加到工程中时选择Create folder references
,以避免后续工程编译时可能的文件名冲突:
- 在工程中添加静态库链接(参照上一章节)
- 在 Build Settings 中设置 Library Search Paths:
${SRCROOT}/lib
,注意要选择recursive
,这个是用来查找 XCFramework 文件。 - 在 Build Settings 中设置 Import Paths:
${SRCROOT}/lib
,注意要选择recursive
,这个是用来查找.swiftmodule
文件,用于确定 Swift 静态库的接口。 - 设置完成后在代码中引用静态库即可使用:
import UIKit
import SwiftStaticLibraryExample
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let title = SwiftStaticLibraryExample().title
print(title)
}
}
- 注意,在 App 项目中 Archive 时可能需要关闭 bitcode,Swift 静态库无法生成 bitcode.
- 在打包的资源中去除
.swiftmodule
中的 x86_64 架构下的文件,在生成 Archive 时不需要这些文件,通过搜索直接删除即可:
总结
XCFramework 本质上与之前的静态库、动态库没有什么区别,封装形式上统一了,使用方法与之前基本一致,以前需要的设置现在一样需要(如头文件、import 方式、swiftmodule 等),但是 Xcode 会逐渐将 XCFramework 整合得更易用,包括与 Swift Package Manager 的结合,这对包管理与第三方库生态发展是更有利的。
本文示例工程源码地址:Github - XCFrameworkExample