版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.01.25 星期一 |
前言
iOS中有很多库,包括系统库还有我们使用的三方封装库,这个专题我们就一起看学习一下相关库的知识。
开始
首先看下主要内容:
了解如何构建iOS框架,该框架可让您在应用之间共享代码,模块化代码或将其作为第三方库分发。内容来自翻译。
下面看下写作环境:
Swift 5, iOS 14, Xcode 12
接着就是正文啦。
您是否曾经想在两个应用程序之间共享大量代码,或者想与其他开发人员共享程序的一部分?
也许您想对代码进行模块化,就像iOS SDK
通过功能将其API分开一样。 或者,您可能想像流行的第三方库一样分发代码。
在本教程中,您将把在 Creating a Custom CalendarControl for iOS的CalendarControl
提取到一个单独的可重用框架中。 在此过程中,您将:
- 为
CalendarControl
创建一个新框架。 - 迁移现有代码。
- 将所有内容导入回应用程序。
- 构建一个二进制框架
XCFramework
。 - 将其打包为超级便携式
Swift Package
。 - 为您的
Swift Package
设置一个存储库并发布它。
待您完成时,该应用程序将像以前一样使用您开发的便携式XCFramework
进行操作!
查找入门项目文件夹。 找到1-RWCalendarPickerUI
并打开RWCalendarPicker.xcodeproj
。
RWCalendarPicker
是一款类似于提醒的清单应用程序,可让用户创建任务并设置截止日期。
构建并运行以了解其工作原理。
查看RWCalendarPicker
中的文件以熟悉该项目。 CalendarControl
的代码分为几类:
- Day.swift:一种数据模型,其中包含每个日对象的数据。
- MonthMetadata.swift:数月的数据模型。
- CalendarDateCollectionViewCell.swift:使用
collection view
中的单元格显示一个月中的几天。 每个单元格都在此处自定义。 - CalendarPickerFooterView.swift:允许用户选择不同的月份。
- CalendarPickerHeaderView.swift:显示当前月份和年份,让用户关闭选择器并显示工作日标签。
- CalendarPickerViewController.swift:日历的主体,在其中结合了所有相关视图。
CalendarPicker非常方便。 在此应用程序之外的多个应用程序中使用它会不会很好? 框架就是可以做到!
What is a Framework?
框架是独立的,可重用的代码和资源块,您可以将其导入许多应用程序。 您甚至可以在iOS,tvOS,watchOS和macOS应用程序之间共享它们。 与Swift的access control
结合使用时,框架可帮助定义代码模块之间强大的,可测试的接口。
框架具有三个主要目的:
- 封装代码
- 模块化代码
- 重用代码
如果您使用其他语言编程,则可能听说过node modules, packages, gems or jars
。 框架与Apple生态系统中的框架相同。 iOS SDK
中常见框架的一些示例包括Foundation,UIKit,SwiftUI,CloudKit
和Combine
。
用Swift的话来说,模块是分布在一起的一组编译后的代码。 框架是模块的一种,而应用程序是另一种。
注意:如果要了解有关框架的更多信息,请阅读What are Frameworks?。
1. Creating a Framework
苹果在Xcode 6
中引入了Cocoa Touch Framework
,最近又将其更改为Framework
。 创建框架从未如此简单。 首先,您将为框架创建项目(project)
。
在Xcode中,选择File ▸ New ▸ Project…
。 然后选择iOS ▸ Framework & Library ▸ Framework
。
点击Next
。 然后将Product Name
设置为CalendarControl
。 使用您自己的Organization Name
和Organization Identifier
。
点击下一步。 在文件选择器中,选择在2-Framework
处创建项目。 然后单击Create
。
现在您有一个项目,尽管很无聊,但却创建了一个框架!
2. Adding the Source Code to the Framework
您当前的状态是一个没有代码的框架。 这和不含糖的纯巧克力一样诱人。 在本部分中,您将通过将现有文件添加到框架中来引入代码。
从RWCalendarPicker
源目录中,将Day.swift
拖动到Xcode中的CalendarControl
项目中。 确保根据需要选中Copy items if needed
,以便将文件复制到新项目中,而不是添加引用。 框架需要自己的代码(而不是引用)才能独立。
仔细检查Day.swift
在CalendarControl
中是否具有Target Membership
,以确保它出现在最终框架中。 通过选择此文件并确保在File inspector
的Target Membership
区域中选择了CalendarControl
来验证这一点。
现在,按照上述步骤将这些文件添加到您的项目中:
- MonthMetadata.swift
- CalendarDateCollectionViewCell.swift
- CalendarPickerFooterView.swift
- CalendarPickerHeaderView.swift
- CalendarPickerViewController.swift
注意:严格来说,将类划分为各自的文件夹组不是必须的,但是这是组织代码的一种很好的做法。
在“项目”导航器中选择项目,然后在Targets
中选择CalendarControl
。 打开Build Settings
。 然后将Build Libraries for Distribution
设置为yes
。 这将产生一个模块接口文件,当有人跳转到Xcode中的模块定义时,该文件将显示您的公共API。
构建框架项目。 确保您没有任何构建警告或错误的情况下获得Build Succeeded
。
3. Adding Framework to the Project
关闭CalendarControl
。 返回到RWCalendarPicker
。 现在,您已经将calendar framework
组装到框架framework
中,您不再需要在主项目中迁移文件。 删除以下文件。 在确认对话框中选择Move to Trash
。
- Day.swift
- MonthMetadata.swift
- CalendarDateCollectionViewCell.swift
- CalendarPickerFooterView.swift
- CalendarPickerHeaderView.swift
- CalendarPickerViewController.swift
构建项目。 您会看到几个可预测的错误,其中Xcode
报警不知道CalendarPickerViewController
到底是什么。 具体来说,您会在范围错误消息中看到Cannot find ‘CalendarPickerViewController’ in scope
。
您可以通过添加CalendarControl
框架项目来解决这些问题。
4. Embedding Your Binary
现在,右键单击项目导航器中的根RWCalendarPicker
节点。 单击Add Files to “RWCalendarPicker”
。
在文件选择器中,导航到2-Framework / CalendarControl
,然后选择CalendarControl.xcodeproj
。 然后单击添加将其添加为子项目。
注意:并非必须将框架项目添加到应用程序项目中。 您可以添加
CalendarControl.framework
输出。但是,将项目组合在一起可以更轻松地同时开发框架和应用程序。 您对框架项目所做的任何更改都会自动传播到应用程序。 这也使Xcode更容易解析路径并知道何时重建项目。
构建并运行。 您会看到相同的编译错误!
即使两个项目现在在一起,RWCalendarPicker
仍然无法获得CalendarControl
。 就像他们坐在同一个房间里一样,但是RWCalendarPicker
无法看到新框架。
请按照以下步骤解决问题:
- 1) 将
scheme
更改为CalendarControl
并进行构建。 - 2) 然后,展开
CalendarControl
项目以查看Products
文件夹。 - 3) 在其下面查找
CalendarControl.framework
。 该文件是框架项目的输出,该项目打包了二进制代码,header
,资源和元数据。 - 4) 然后选择顶层
RWCalendarPicker
节点以打开项目编辑器。 - 5) 单击
RWCalendarPicker target
。 然后转到General
选项卡。 - 6) 向下滚动到
Frameworks, Libraries and Embedded Content
部分。 - 7) 将
CalendarControl.framework
从CalendarControl.xcodeproj
的Products
文件夹中拖到此部分。
您在Frameworks, Libraries and Embedded Content
中为框架添加了一个条目entry
。
现在,该应用程序知道了框架以及在何处可以找到它。 这样就足够了吧?
切换到RWCalendarPicker scheme
并构建项目。 更多相同的错误。
您错过了框架开发的重要部分:Access Control。
Access Control
尽管框架是项目的一部分,但是项目的代码对此并不了解。 看不见,也想不到。
转到ItemDetailViewController.swift
,并将以下行添加到文件顶部的导入列表中:
import CalendarControl
至关重要的是,此包含仍然不能解决构建错误,因为Swift
使用access control来确定构造是否对其他文件或模块可见。
默认情况下,Swift
将所有内容内部化(internal)
-仅在其自己的模块中可见。
要恢复该应用程序的功能,您必须更新CalendarControl
类上的访问控制。
尽管有点乏味,但是更新访问控制的过程通过隐藏不需要出现在框架外部的代码来提高模块化。您可以通过使某些函数不具有访问修饰符或明确声明它们internal
来实现此目的。
Swift
具有五个级别的access control
。创建自己的框架时,请遵循以下经验法则:
- Open and public:适用于应用或其他框架(例如自定义视图)调用的代码。
- Internal:用于在框架内的函数和类之间使用的代码,例如该视图中的自定义层。
- Fileprivate:用于单个文件中使用的代码,例如用于计算布局高度的辅助函数。
- Private:用于封闭声明中使用的代码,例如单个类
class
块和同一文件中该声明的扩展。
当CalendarControl
成为RWCalendarPicker
应用程序的一部分时,内部访问就不成问题。现在,它位于单独的模块中,您必须将其公开,以供应用使用。您将在下一部分中进行操作。
注意:如果要了解有关访问控制的更多信息并了解
open
和public
之间的区别,请查看 Access Control Documentation。
1. Updating the Framework Access Level
打开CalendarPickerViewController.swift
。通过将public
关键字添加到类定义中来使该类成为公共类,如下所示:
public class CalendarPickerViewController: UIViewController {
现在,CalendarPickerViewController
对导入CalendarControl
框架的任何应用程序文件都是可见的。
接下来,将public
关键字添加到:
CalendarPickerViewController.init(baseDate:selectedDateChanged :)
CalendarPickerViewController.init(coder :)
CalendarPickerViewController.viewDidLoad()
CalendarPickerViewController.viewWillTransition(to:with :)
CalendarPickerViewController.collectionView(_:numberOfItemsInSection :)
CalendarPickerViewController.collectionView(_:cellForItemAt :)
CalendarPickerViewController.collectionView(_:didSelectItemAt :)
CalendarPickerViewController.collectionView(_:layout:sizeForItemAt :)
注意:您可能想知道为什么必须将
init
声明为public
。 Apple在其Access Control Documentation中解释了访问控制的这一点和其他一些要点。
构建并运行。 现在,您获得了CalendarControl
。
恭喜你! 现在,您有了一个有效的独立框架和使用该框架的应用程序!
Publishing the XCFramework
您可能在WWDC 2019
期间听说过XCFramework
。是的,您是对的:这是可以使用Xcode生成的二进制框架的名称。
在2019
年之前,您只有一个机会来制作自己的二进制框架:Universal Static Library
,也称为Fat Framework
。
为了支持多种架构,例如模拟器和设备,您必须将它们组合在fat
框架的一个库下。 但是,在本文之后,您的框架不必再胖了。
1. Archiving Your Framework
在本部分中,您将与老朋友Terminal
一起工作。 hoo!
打开您的终端,并使用以下命令导航到framework
文件夹。 或者,您可以在cd
命令后将项目文件夹拖到终端:
cd "CreateFrameworkForiOS/starter/2-Framework"
接下来,开始为以下targets
归档您的框架:
iOS
Simulator
macOS
从iOS开始。 在终端中输入以下命令:
xcodebuild archive \
-scheme CalendarControl \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath './build/CalendarControl.framework-iphoneos.xcarchive' \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
该命令将使用以下列表作为输入来生成您的框架的存档:
- 1)
-scheme CalendarControl
:它将使用此scheme
进行archiving
。 - 2)
-configuration Release
:它将使用发布配置进行构建。 - 3)
-destination“ generic / platform = iOS”
:这是架构类型。 - 4)
-archivePath
:它将archives
以给定名称保存到该文件夹路径中。 - 5)
SKIP_INSTALL
:设置为NO
即可将框架安装到archive
中。 - 6)
BUILD_LIBRARIES_FOR_DISTRIBUTION
:确保为分发而构建您的库并创建接口文件。
接下来,目标Simulator
。 通过将此命令添加到终端来进行归档:
xcodebuild archive \
-scheme CalendarControl \
-configuration Release \
-destination 'generic/platform=iOS Simulator' \
-archivePath './build/CalendarControl.framework-iphonesimulator.xcarchive' \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
这些命令选项与iOS的选项相同,不同之处在于:
- 1)
-destination“ generic / platform = iOS Simulator”
:在此处设置架构类型。 - 2)
-archivePath
:这会将archive
生成到具有给定名称的文件夹路径中。
最后,为macOS
生成一个新的存档。 在终端中添加以下命令:
xcodebuild archive \
-scheme CalendarControl \
-configuration Release \
-destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst' \
-archivePath './build/CalendarControl.framework-catalyst.xcarchive' \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
该命令与其他命令相同,不同之处在于:
- 1)
-destination‘platform = macOS,arch = x86_64,variant = Mac Catalyst'
:在此指示体系结构类型。 - 2)
-archivePath
:这会将archive
生成到具有给定名称的文件夹路径中。
如您在查找器和以下屏幕截图中所见,您从框架中生成了三个不同的存档文件。
2. Generating the XCFramework
现在,创建二进制框架XCFramework
。 将以下命令添加到终端:
xcodebuild -create-xcframework \
-framework './build/CalendarControl.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/CalendarControl.framework' \
-framework './build/CalendarControl.framework-iphoneos.xcarchive/Products/Library/Frameworks/CalendarControl.framework' \
-framework './build/CalendarControl.framework-catalyst.xcarchive/Products/Library/Frameworks/CalendarControl.framework' \
-output './build/CalendarControl.xcframework'
此命令使用您生成的先前archives
将XCFramework
添加到build
文件夹。
检查构建文件夹以查看XCFramework
包含的内容。
嘘! 现在,您已经完成了第一个XCFramework
。
3. Integrating XCFramework Into Your Project
现在该从RWCalendarPicker
项目中删除CalendarControl
引用,如下所示:
这样做是因为您不再需要访问框架源代码。 将XCFramework
拖到项目target
的Frameworks, Libraries and Embedded Content
:
构建并运行。 您将拥有与以前相同的类的访问权限,但是这次您的框架仅是二进制的。
Distributing XCFramework as a Swift Package
在WWDC 2020
上,Apple宣布您可以轻松地在Swift Packages
中分发XCFramework
。 那不是很棒吗?
注意:如果您不熟悉
Swift Packages
或Swift Package Manager
,可以通过阅读Swift Package Manager for iOS来了解更多信息。
您应该有一个Swift Package
来分发XCFramework
。 您将在下一部分中创建一个。 然后,您可以通过在GitHub
上发布框架来共享您的框架。
1. Preparing the Swift Package
在入门项目文件中,您已经有一个简单的Swift Package
。 导航到/ starter / 3-SwiftPackage / CalendarControl
并打开Package.swift
此类是您的Swift Package
的清单。 您需要对其进行修改,以使CalendarControl
成为Swift Package
。
请按照下列步骤,并用正确的值填充清单:
- 1)
Platforms
:
platforms: [
.macOS(.v10_15), .iOS(.v14), .tvOS(.v14)
],
此代码指示它可以在哪些平台上运行。
- 2)
Products
:
products: [
.library(
name: "CalendarControl",
targets: ["CalendarControl"]),
],
这些是包装提供的产品。 这些可以是库(可以导入其他Swift项目中的代码)或可执行文件(可以由操作系统运行)。 product
是您可以导出以供其他软件包使用的target
。
- 3)
Targets
:
targets: [
.binaryTarget(
name: "CalendarControl",
path: "./Sources/CalendarControl.xcframework")
]
Targets
是独立构建的代码模块。 在这里,您可以为XCFramework
添加路径。
注意:清单的第一行必须包含格式化的注释,该注释告诉
Swift Package Manager
构建软件包所需的最低Swift
编译器版本。
最后,将XCFramework
添加到项目中并将其放在Sources
下:
恭喜你! 您的Swift Package
已准备好分发。
2. Large Binary and Compute Check-Sum
尽管CalendarControl
并非如此,但特别庞大的框架仍需要格外小心。 如果您想了解有关处理较大二进制文件的更多信息,请打开下面的spoiler
。
如果您有大型二进制框架(如
Apple
所建议的那样),则不应将其添加到存储库中。 相反,您可以将其上传到主机,并在manifest target
中添加一个URL,如下所示:targets: [ .binaryTarget( name: "CalendarControl", >url:"https://example.com/CalendarControl.xcframework.zip", checksum: "4d4053074fd8b92f9f2f339c48980b99") ]
这里有三个输入:
- 1)
Name
:这是您的包名称。- 2)
URL
:您的XCFramework
压缩文件URL
。- 3)
Checksum
:您的Swift Package
将确保框架zip
文件与通过检查此checksum
和所生成的压缩文件相同。请按照以下步骤计算校验和
checksum
:
- 1) 压缩您的框架,然后将
zip
文件复制到Swift Package
文件夹中。- 2) 在终端中,导航到
Swift Package
文件夹。- 3) 运行以下命令并生成校验和。
swift package compute-checksum CalendarControl.xcframework.zip
您的
XCFramework
校验和在这里。 您可以将其复制到manifest
。
3. Publishing Your Swift Package
Xcode使发布软件包变得容易。 接下来,您将发布您创建的CalendarControl Swift Package
。
使用Xcode打开Swift Package
项目。 然后从菜单栏中选择Source Control ▸ New Git Repositories…
。
这将在您的计算机上创建一个Git
存储库,并首次提交代码。
必须先将其公开发布,然后才能发布您的软件包供他人使用。 最简单的方法是发布到GitHub
。
如果您没有GitHub
帐户,则可以在github.com上免费创建一个。 然后,如果还没有这样做,请通过在Xcode菜单中选择Xcode ▸ Preferences
,将GitHub帐户添加到Xcode。 选择Accounts
。
现在,单击+
添加一个新帐户。 选择GitHub
并按要求填写您的凭据。
打开Source Control navigator
,然后选择CalendarControl
程序包。 然后,通过右键单击或按住Control
键单击打开上下文菜单。
接下来,选择New “CalendarControl” Remote…
。 您可以将可见性更改为Private
或接受默认设置。 然后单击Create
。
这将在GitHub
上创建一个新的存储库,并自动为您推送代码。
接下来,通过为包创建标签来设置框架的版本。 在上下文菜单中,选择Tag main…
。 将其标记为1.0.0
版本,然后单击Create
。
最后,从Xcode菜单栏中选择Source Control▸Push…
。 确保选择了Include tags
。 然后单击Push
。
这会将标签推送到SwiftPM
可以读取它的GitHub
。 您的软件包的1.0.0
版现已发布。
Using Swift Package
现在是时候在RWCalendarPicker
项目中使用您的Swift Package
了。
再次打开RWCalendarPicker
。 从项目target
中删除导入的CalendarControl
框架:
接下来,您将学习如何使用Swift Package Manager
将Swift Package
添加到项目中。
1. Adding Package Dependency Using SPM
选择File ▸ Swift Packages ▸ Add Package Dependency…
。 粘贴Git Repository URL
。 单击下一步。
根据您的GitHub设置,您可能需要在此处验证SSH
密钥。 然后,在Rules
下,确保为1.0.0
版本选择了Up to Next Major
。 点击Next
。
如果您想了解有关主要和次要版本控制的更多信息,请访问semver.org。 Xcode提取包后,请确保已选择CalendarControl
产品并将其添加到RWCalendarPicker target
。 然后选择Finish
。
构建并运行。 确保一切都像以前一样运行。
太棒了! 现在,您有一个在项目内部使用的远程Swift软件包。
在本教程中,您学习了如何:
- 1) 从
RWCalendarPicker
中提取CalendarControl
并创建一个新框架。 - 2) 在
RWCalendarPicker
中使用CalendarControl
框架。 - 3) 建立一个二进制框架,并在
RWCalendarPicker
中使用该XCFramework
。 - 4) 发布一个
Swift Package
,并在RWCalendarPicker
中使用它。
干得好!
从这里,观看有关此主题的WWDC 2019
和2020
视频:
- Distribute Binary Frameworks as Swift Packages
- Binary Frameworks in Swift
后记
本篇主要讲述了
iOS Framework
的创建的简单示例,感兴趣的给个赞或者关注~~~