XCFramework 深度使用(1)-Swift 静态库

6,332 阅读3分钟

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