Xcode 11和XCFrameworks:一种新的框架打包格式


在拥有并开发自己的库和组件堆栈的许多公司的生命中,有时会很难维护该堆栈和组件的数量。


对于iOS平台(通常是Apple生态系统)的开发,有两种选择可以将库连接为依赖项:


  1. 每次您构建应用程序时,请收集它们。
  2. 使用已经收集的依赖项提前收集它们。

当选择第二种方法时,使用CI / CD系统将库组装成现成的工件变得合乎逻辑。


但是,在构建库以及使用该库的最终产品时,需要为Apple生态系统中的多个平台或处理器体系结构构建库通常并不总是需要琐碎的操作。


在这种背景下,很难不注意到,研究Apple在WWDC 2019上提出的一项创新非常有趣,这是SwiftBinary Frameworks演示的一部分- 框架的包装格式是XCFramework。


与已建立的方法相比,XCFramework具有多个优点:


  1. 开箱即用的捆绑包中所有目标平台和体系结构的依赖性打包。
  2. XCFramework格式的连接捆绑包,作为所有目标平台和体系结构的单个依赖项。
  3. 无需构建胖/通用框架。
  4. 在将最终应用程序加载到AppStore之前,无需摆脱x86_64 slice。

在本文中,我们将解释为什么引入这种新格式,它是什么,以及它给开发人员带来的好处。


新格式如何出现


苹果先前发布了Swift Package Manager依赖管理器
最重要的是,Swift PM允许您以开放源代码的形式提供库,并带有相关性的描述。


从提供库的开发人员的角度来看,我想强调一下Swift PM的两个方面。


  • 明显的缺点是,出于某种原因,并非所有的图书馆供应商都希望向消费者开放其源代码。
  • 一个明显的优点-从源代码编译依赖项时,我们无需观察库的二进制兼容性。

XCFramework Apple提供了一种新的打包库二进制格式,认为它可以替代Swift包。


从Xcode 11及其beta版本开始,可以使用这种格式以及连接XCFramework中组装的库的功能。


什么是XCFramework


XCFramework的核心是一种打包和交付各种版本库的新方法。


除其他外,新格式还允许将静态库及其头文件(包括用C / Objective-C编写的头文件)打包在一起。


更详细地考虑格式。


  1. 开箱即用的捆绑包中所有目标平台和体系结构的依赖关系打包


    现在,可以将每个目标平台和体系结构的所有库程序集打包为扩展名为.xcframework的单个软件包。
    但是,为此,此时,您必须使用脚本通过Xcode 11的新-create-xcframework开关来调用xcodebuild


    组装和包装过程将进一步考虑。


  2. XCFramework格式的连接包,作为所有目标平台和体系结构的单个依赖项


    由于包.xcframework包含所有必需的依赖项汇编选项,因此我们不必担心其体系结构和目标平台。


    在Xcode 11中,就像常规.framework一样,连接了.xcframework打包的库。
    更具体地说,这可以在目标设置中通过以下方式实现:


    • 将.xcframework添加到“常规”选项卡的“框架和库”部分
    • 在“构建阶段”选项卡上将.xcframework添加到“将二进制文件与库链接”

  3. 无需构建胖/通用框架


    以前,为了在插件库中支持几个库和几个体系结构,必须准备所谓的胖框架或通用框架。
    这是使用lipo将组装好的框架的所有选项缝到一个厚的二进制文件中完成的。


    对此的更多详细信息,例如,在以下文章中:



  4. 在AppStore中加载最终应用程序之前,无需摆脱x86_64 slice


    通常,这种切片用于在iOS模拟器中提供库。
    当您尝试在AppStore中下载依赖项包含x86_64 slice的应用程序时,可能会遇到众所周知的错误ITMS-90087



创建和打包XCFramework:理论


在前面提到的演示文稿中,需要执行几个步骤才能以XCFramework格式组装和打包库:


  1. 项目准备


    首先,在负责构建目标平台库的所有项目目标区域中,您需要启用Xcode 11的新的Build Libraries for Distribution设置。



  2. 目标平台和体系结构的项目组装


    接下来,我们必须收集目标平台和体系结构的所有目标。
    让我们考虑使用特定项目配置示例调用命令的示例。


    假设在项目中,我们有两个方案“ XCFrameworkExample-iOS”和“ XCFramework-macOS”。



    此外,在该项目中,有两个目标可收集iOS和macOS的库。



    要构建所有必需的库配置,我们需要使用相应的方案来收集两个目标。
    但是,对于iOS,我们需要两个程序集:一个用于终端设备(ARM),另一个用于模拟器(x86_64)。


    总共,我们需要收集3个框架。


    为此,可以使用xcodebuild命令:


     # iOS devices xcodebuild archive \ -scheme XCFrameworkExample-iOS \ -archivePath "./build/ios.xcarchive" \ -sdk iphoneos \ SKIP_INSTALL=NO # iOS simulator xcodebuild archive \ -scheme XCFrameworkExample-iOS \ -archivePath "./build/ios_sim.xcarchive" \ -sdk iphonesimulator \ SKIP_INSTALL=NO # macOS xcodebuild archive \ -scheme XCFrameworkExample-macOS \ -archivePath "./build/macos.xcarchive" \ SKIP_INSTALL=NO 

    结果,我们得到了3个组装好的框架,我们将它们进一步包装在.xcframework容器中。


  3. 将组装的.framework包装在.xcframework中


    您可以使用以下命令执行此操作:


     xcodebuild -create-xcframework \ -framework "./build/ios.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/ios_sim.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/macos.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -output "./build/XCFrameworkExample.xcframework" 

    可能会有很多-framework参数-framework ,这些-framework指向您要附加到.xcframework容器的所有.framework程序集。



为将来的XCFramework组装和包装准备图书馆项目


TL; DR:可以从Github上的存储库下载完成的项目。


例如,我们正在实现一个可用于两个平台的库:iOS和macOS。
我们将使用本文上一节中提到的项目配置:两个方案和两个对应的iOS和macOS平台框架目标。


库本身会为我们提供String?的简单扩展String?Optional where Wrapped == String ),具有单个属性。


我们将此属性isNilOrEmpty ,顾名思义,在String?内时它会告诉我们String? 缺少值或内部存储的字符串为空。


该代码可以实现如下:


 public extension Optional where Wrapped == String { var isNilOrEmpty: Bool { if case let .some(string) = self { return string.isEmpty } return true } } 

我们直接进行项目的创建和配置。


  1. 首先,我们需要为您选择的两个目标平台之一创建一个“框架”类型的项目:iOS或macOS。


    您可以通过菜单项“ File” =>“ New” =>“ Project”,或使用键盘快捷键⇧++ N (默认)在Xcode中执行此操作。


    接下来,在对话框顶部,选择所需的平台(iOS或macOS),选择Framework项目的类型,然后转到“下一步”按钮。


    在下一个屏幕上,我们需要在“产品名称”字段中设置项目名称。


    或者,您可以使用项目的“基本”名称,在前面提到的配置中,它是“ XCFrameworkExample”。


    将来,在配置项目时,我们将在目标名称中使用的基本名称中添加表示平台的后缀。


  2. 之后,您需要在项目中为列出的另一个平台创建另一个“框架”类型的目标(最初创建该项目的平台除外)。


    为此,请使用菜单项“文件” =>“新建” =>“目标”。


    接下来,我们在对话框中选择两个平台的另一个(相对于第1段中选择的那个),然后再次选择项目类型“框架”。


    对于“产品名称”字段,我们可以立即在平台后缀中使用该名称,在本段中为其添加目标。 因此,如果平台是macOS,则名称可以是“ XCFrameworkExample-macOS”(%base_name%-%platform%)。


  3. 我们将设置目标和图表,以使其易于区分。


    首先,重命名我们的方案及其附属的目标,以使它们的名称反映平台,例如:


    • “ XCFrameworkExample-iOS”
    • “ XCFrameworkExample-macOS”

  4. 接下来,将带有扩展名String?的文件添加到项目.swift中String?


    将一个新的.swift文件添加到项目中,名称为“ Optional.swift”。
    在文件本身中,我们将前面提到的扩展名放在Optional


    重要的是不要忘记将代码文件添加到两个目标中。




现在我们有了一个项目,可以使用上一阶段中的命令将它们放在XCFramework中。


以.xcframework格式组装和打包库的过程


在此阶段,您可以在单独的文件中使用bash脚本来构建库并将其打包为.xcframework格式。 另外,这将允许将来使用这些开发将解决方案集成到CI / CD系统中。


该脚本看起来很简单,实际上,它汇集了前面提到的汇编命令:


 #!/bin/sh # ---------------------------------- # BUILD PLATFORM SPECIFIC FRAMEWORKS # ---------------------------------- # iOS devices xcodebuild archive \ -scheme XCFrameworkExample-iOS \ -archivePath "./build/ios.xcarchive" \ -sdk iphoneos \ SKIP_INSTALL=NO # iOS simulator xcodebuild archive \ -scheme XCFrameworkExample-iOS \ -archivePath "./build/ios_sim.xcarchive" \ -sdk iphonesimulator \ SKIP_INSTALL=NO # macOS xcodebuild archive \ -scheme XCFrameworkExample-macOS \ -archivePath "./build/macos.xcarchive" \ SKIP_INSTALL=NO # ------------------- # PACKAGE XCFRAMEWORK # ------------------- xcodebuild -create-xcframework \ -framework "./build/ios.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/ios_sim.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -framework "./build/macos.xcarchive/Products/Library/Frameworks/XCFrameworkExample.framework" \ -output "./build/XCFrameworkExample.xcframework" 

.xcframework内容


作为本文前面段落中的汇编脚本的结果,我们得到了梦co以求的捆绑包.xcframework,可以将其添加到项目中。


如果我们查看此包(与.framework一样,实际上是一个简单的文件夹)的内部,我们将看到以下结构:



在这里,我们看到.xcframework内部是.framework格式的程序集,并按平台和体系结构进行了细分。 另外,为了描述包.xcframework的内容,内部还有一个Info.plist文件。


Info.plist文件具有以下内容
 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>AvailableLibraries</key> <array> <dict> <key>LibraryIdentifier</key> <string>ios-arm64</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>arm64</string> </array> <key>SupportedPlatform</key> <string>ios</string> </dict> <dict> <key>LibraryIdentifier</key> <string>ios-x86_64-simulator</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>x86_64</string> </array> <key>SupportedPlatform</key> <string>ios</string> <key>SupportedPlatformVariant</key> <string>simulator</string> </dict> <dict> <key>LibraryIdentifier</key> <string>macos-x86_64</string> <key>LibraryPath</key> <string>XCFrameworkExample.framework</string> <key>SupportedArchitectures</key> <array> <string>x86_64</string> </array> <key>SupportedPlatform</key> <string>macos</string> </dict> </array> <key>CFBundlePackageType</key> <string>XFWK</string> <key>XCFrameworkFormatVersion</key> <string>1.0</string> </dict> </plist> 

您可能会注意到,与“ .framework”格式相反,“ CFBundlePackageType”键使用的是新值“ XFWK”,而不是“ FMWK”。


总结


因此,XCFramework中的库打包格式只不过是.framework格式编译库的常规容器。


但是,这种格式允许您分别存储和独立使用其中提供的每个体系结构和平台。 这消除了构建胖/通用框架的广泛方法中固有的许多问题。


尽管如此,目前在实际项目中使用XCFramework的问题还是有一个重要的细微差别-依赖关系管理,Apple尚未以XCFramework格式实现。


为此,习惯使用Swift PM,Carthage,CocoaPods和其他依赖项管理系统及其组件。 因此,不足为奇的是,在CocoaPodsCarthage项目中已经确切地开始了对新格式的支持。

Source: https://habr.com/ru/post/zh-CN475816/


All Articles