原文:An Introduction to the Swift Package Manager
作者: Mikael Konutgan
译者:kmyhy

Swift 包管理器的正式发布是随着 Swift3.0 一起发布的,它是一个用于构建能够运行在 macOS 和 Linux 上的 Swift 库和 app 的新方法。它能够帮助你管理依赖,让你轻松构建、测试和运行你的 Swift 代码。

Swift 包管理器有助于极大地改进 Swift 生态系统,让 Swift 更容易使用、部署到没有 Xcode 的平台上,比如 Linux。Swift 包管理器还能解决在使用多个相互依赖的库时的“依赖地狱”的问题。

重要的一点是,因为 Swift 3 的原因,Swift 包管理器只能在 host 平台上编译。换句话说,你无法编译、使用 iOS、watchOS 和 tvOS 上的包。

让我们开始吧!

开始

在开始之前,确认你已经安装了 Swift 3.0 以上。Swift 3 内置在 Xcode8+,因此如果你已经安装了 Xcode8 或以上,你就可以开始本教程。当然实际上你并不需要用 Xcode 去完成本教程,你可以直接从 swift.org 下载安装 Swift 3。

打开终端窗口,输入 swift package。你会看到这个命令的介绍。你将使用这几个命令:

  1. swift package init ,创建新包
  2. swift package update ,更新一个包的依赖
  3. swift package generate-xcodeproj ,为包生成 Xcode 项目

要学习 Swift 包管理器,我们将编写一个命令行 app,用一个库打印出所有国家的国旗字符。首先我们会创建一个可执行包。这种包也就是命令行 app。Swift web app 也属于这种类别。

通过以下命令,创建一个名为 Flag 的可执行包:

mkdir Flag
cd Flag
swift package init --type executable

在运行 swift package init 命令行时,切换到 Flag 目录是关键的,因为 Flag 将作为包名。你会看到输出中会显示一些文件和文件夹,它们是自动生成的。现在来熟悉一下项目结构:

https://koenig-media.raywenderlich.com/uploads/2016/12/Flag-%E2%94%9C%E2%94%80%E2%94%80-Package.swi_.png’ width= ‘300’/>
  1. Package.swift 包含包的描述信息,还包含包的依赖。
  2. Sources/,如名称所示,用于存放你的 Swift 源文件。里面会生成一个 main.swift 文件。这是应用程序的入口。现在,它只会打印 hello world。
  3. Tests/ ,包含单元测试,你可以用 XCText 编写这些测试。待会你会写测试代码。

回到终端窗口,运行:

swift build

这会编译包并在 .build/debug/ 创建一个可执行文件 Flag。运行这个 app,只需要:

.build/debug/Flag

你会在屏幕上看到 Hello,world!

恭喜你!你创建和编译通过了你的第一个 Swift 包!

创建库

要真正能够生成某个国家的国企字符,我们需要编写一个名为 Atlas 的库。然后在你的 Flag app 中调用这个库。

切到 Flag 包之外的目录,通过终端命令创建一个库:

cd ..
mkdir Atlas
cd Atlas
swift package init --type library

包管理器会创建几个文件和文件夹。

https://koenig-media.raywenderlich.com/uploads/2017/01/atlas-library-structure.png’ width=’300’/>

这次,main.swift 会被 Atlas.swift 所替代。这个文件以及在 Sources/ 目录下的文件都会被导入到这个库中。实际上,库和可执行包的区别就在于有没有 main.swift。

这次我们需要用一个测试。用 swift test 来运行这个测试。Swift 包管理器会编译库并运行测试。

注意:你会在 Tests/ 目录下看到 LinuxMain.swift 文件,在 AtlasTests 的测试案例类中还有一个 allTests 属性。对于要在 Linux 上运行 XCTest 测试而言,这两者都是必须的。Linux 没有 O-C 运行时,而 O-C 运行时会自动查找以 test 开头的方法并运行它。每当添加一个测试方法,你都需要修改这个 allTests 属性,每当添加一个新的测试案例类,你都需要修改 LinuxMain.swift。

打开 Atlas.swift 修改内容为:

public struct Country {
  public let code: String

  public init(code: String) {
    self.code = code.uppercased()
  }

  public var emojiFlag: String {
    return "\u{1f1f5}\u{1f1f7}"
  }
}

这里我们实现了一个 Country 结构,它通过一个 ISO 国别代码进行初始化。emojiFlag 属性根据国别代码返回对应的国旗字符。现在,你需要编写测试。

注意每个方法和属性都标记为公有的,这样它的每个成员对于使用这个库的代码来说都是课件的:]

打开 AtlasTests.swift,编辑内容为:

import XCTest
@testable import Atlas  class AtlasTests: XCTestCase {
  func testAustria() {
    XCTAssertEqual(Country(code: "AT").emojiFlag,"\u{1f1e6}\u{1f1f9}")
  }

  func testTurkey() {
    XCTAssertEqual(Country(code: "TR").emojiFlag,"\u{1f1f9}\u{1f1f7}")
  }

  func testUnitedStates() {
    XCTAssertEqual(Country(code: "US").emojiFlag,"\u{1f1fa}\u{1f1f8}")
  }
}

extension AtlasTests {
  static var allTests : [(String,(AtlasTests) -> () throws -> Void)] { return [ ("testAustria",testAustria),("testTurkey",testTurkey),("testUnitedStates",testUnitedStates) ] } }

这里实现了 3 个测试。我们创建了 3 个不同的国籍,然后断言它们是否拥有正确的 emoji 国旗字符。

运行测试:

swift test

你会看到 3 个测试被运行,但全部失败。这说明我们还有很多事情要干啊 :]

现在我们测试失败了,我们要让它们通过测试。

emoji 国旗的工作原理非常简单:传入一个国家代码,比如 AT,然后将每个字符转换成设为的地区指示标志。例如, �� 和 ��。然后将二者组合在一起,你就会得到 emoji 国旗!

https://koenig-media.raywenderlich.com/uploads/2017/01/ragecomic_flags.png’ width=’600’/>

回到 Atlas.swift 为 Country 结构增加一个方法:

func regionalIndicatorSymbol(unicodeScalar: UnicodeScalar) -> UnicodeScalar? {
  let uppercaseA = UnicodeScalar("A")!
  let regionalIndicatorSymbolA = UnicodeScalar("\u{1f1e6}")!
  let distance = unicodeScalar.value - uppercaseA.value
  return UnicodeScalar(regionalIndicatorSymbolA.value + distance)
}

这里,我们利用了字母和区域指示标志在 Unicode 表中的值都是连续排列的原理。比如 A 是 65,B 是 66,而 �� 是 127462,�� 就是 127463。如果将字符 P 转换成区域指示标志,需要计算出 A 到 P 的值相差多少,然后将 ��加上这个差值就得到 ��。

这是最难的部分。写好这个方法后,剩下的事情就简单了。将 emojiFlag 属性定义修改为:

public var emojiFlag: String {
  return code.unicodeScalars.map { 
  String(regionalIndicatorSymbol(unicodeScalar: $0)!) } .joined()
}

我们将国家代码的每个字母放到数组中,然后将每个字母转换成对应的区域指示标志,然后将它们连在一起。这就得到了国旗!

运行测试,3 个测试都通过了。

接下来将代码提交到 Git,并标记一个本号。因为这是我们的第一个版本,我们可以将版本标记为 1.0.0。

执行下列命令,创建 Git 存储库并标记版本:

git init
git add .
git commit -m "Initial commit"
git tag 1.0.0

创建可执行包

现在你已经准备好 Flag 库,我们可以将它添加为 Flag 可执行包的依赖。

回到 Flag 目录,打开 Package.swift 文件。它目前是这个样子:

import PackageDescription

let package = Package(
  name: "Flag"
)

每个 Swift 包都有一个类似的 PackageDescription。最重要的参数是 dependencies 参数。

将包描述修改为这样:

let package = Package(
  name: "Flag",dependencies: [
    .Package(url: "../Atlas","1.0.0")
  ]
)

这里,我们声明 Flag 包拥有一个依赖,这个依赖的 URL 是 ../Atlas,版本是 1.0.0。

版本号应该使用语义上的版本号。简单说,就是类似于 MAJOR.MINOR.PATCH 这样的版本号。MAJOR 版本表示的是不向后兼容的修改,MINOR 版本表示向后兼容的修改,PATCH 版本是 bug 的修复。关于语义版本,细节可参考 这里。

大部分情况下,你只想让新版本在 bug 修复和小版本改进上做修改。幸好,Swift 包管理器允许我们这样做。将包描述修改为:

let package = Package(
  name: "Flag",majorVersion: 1)
  ]
)

包管理器提供了你在更新库时想精确控制的版本。请参考这里。

编译包:

swift build

Swift 包管理器将抓取、编译库并将之连接到你的可执行包。现在的文件夹结构将是这个样子:

https://koenig-media.raywenderlich.com/uploads/2017/01/flag-structure.png’ width=’300’/>

你会看到 Swift 包管理器已经细心地根据你的要求将 1.0.0 版本安装到项目中了。打开 main.swift 文件,编辑内容如下:

import Atlas

let arguments = CommandLine.arguments

if arguments.count != 2 {
  print("USAGE: flag [iso country code]")
} else {
  let code = arguments[1]
  let country = Atlas.Country(code: code)
  print(country.emojiFlag)
}

这里,我们导入了 Atlas 库,根据命令行参数的第一个参数,打印出对应的国旗 emoji。如果缺少参数,我们会打印命令帮助。

编译运行 app:

swift build
./.build/debug/Flag US

你在终端窗口中看到了美国国旗!

在你和你的 app 玩得不亦乐乎的时候,我们该来打包它 了。最后编译一下 app,这次需要加上 release 优化参数:

swift build --configuration release

现在你可以这样运行你的 release 版 app 了:

./.build/release/Flag PR

你可以将 ./.build/release/Flag 文件打包压缩,然后分享给你的朋友、家人或其它人了 :]

用包管理器生成 Xcode 项目

老旧的命令行和文本编辑器很酷,但你可能是一个 iOS 或 macOS 程序员,你使用的是 Xcode。别担心——大部分工作 Xcode 都可以做得很好。

回到 Atlas 包下面,生成一个 Xcode 项目:

cd ../Atlas
swift package generate-xcodeproj

这会生成一个 Atlas.xcodeproj 文件。你可以用 Xcode 打开这个项目,像其他 Xcode 项目一样编译包和运行测试。

https://koenig-media.raywenderlich.com/uploads/2017/01/xcode-650x357.png’ width= ‘600’/>

同样的方法可以用在 Flag 包上。在 Flag 文件夹下面执行 swift package generate-xcodeproj 命令,生成 Flag.xcodeproj。

cd ../Flag
swift package generate-xcodeproj

用 Xcode 打开项目,确认 Flag 可执行目标是否选中,它显示一个小的终端窗口的图标。现在你也可以编译和运行这个包了。

为了指定可执行命令的参数,请进入 Product\Scheme\Edit Scheme… 窗口,选择 Run\Arguments ,在 Arguments Passed On Launch 中添加一个参数比如 US:

https://koenig-media.raywenderlich.com/uploads/2017/01/Flag_xcodeproj-650x361.png’ width=’600’/>

注意,Xcode 无法添加和编译依赖,因此你无法完全离开命令行。

结束

你可以从这里下载完成的 Swift 包管理器项目。

你还可以参考Swift 官网关于包管理器的章节。

关于包描述选项的最新文档,请参考github。

关于 Swift 4 的包管理器修改方向,你可以参考 Swift 演进路线图的邮件列表。

你还可以参考IBM 的 Swift Package Catalog,它可以帮助你找到可以用在你项目中的新包。

在 Swift 包管理器能够支持非主机平台之前,你仍然不得不使用 Cocoapods 或 Carthage 来构建 iOS、watchOS 和 tvOS app。

留作今天的作业,你可以将你的库推到 GitHub,然后在 Atlas 中使用它的远程依赖。提示:只需要将 dependency 的 url 参数修改为 GitHub URL。

尝试添加新的功能,比如当没有提供任何参数时,列出所有国家的名字和国旗。提示:你将需要用到 Locale.isoRegionCodes。

在 Atlas 库中实现你的新功能,然后创建新的版本,比如 1.1.0,然后在 Flag 中使用这个新版本。确认你在包描述中使用了新版本,然后通过 Swift 包管理器将依赖升级到新版本。

将你的答案公布到下面的留言中,祝你开心!

Swift 包管理器教程的更多相关文章

  1. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  2. ios – Xcode在发布时崩溃

    我正在开发iOS应用程序,我在应用程序中没有更改任何内容.但是在重新启动我的Mac时,XCode每次都会崩溃.不知道这是否重要,但我的聚光灯找不到任何应用程序当我打开取景器并使用快捷键cmdshiftH时我看到一个白色的窗户.Xcode不再启动并抛出此错误:我希望有一个人可以帮助我.这会导致与权限冲突吗?

  3. xcode找不到匹配的配置文件

    我有一个AdhociOS应用程序,它给了我“在xcode6中找不到匹配的配置文件”,我创建了一个Adhoc配置文件,下载它,双击它并在General–Identity下选择了一个团队.但我接着得到了那条消息,并尝试使用“修复问题”按钮没有帮助.在构建设置–供应配置文件–发布我有“自动”.任何人都可以帮助我,我完全迷失了……

  4. iOS:调试无法在XCode中运行

    我正在使用XCode4和iOSSDK4.3.我的调试工作正常,但现在我发现在设置断点时,应用暂停,但XCode不关注编辑器中的行.点击断点时也不会显示绿色箭头.我有一个在AppDelegate中分配的UINavigationController.当我在didFinishLaunchingWithOptions中设置断点时,一切都按预期工作:这是当一个断点暂停了mapViewController中的

  5. ios – 如何/是否在Xcode中制作通用故事板

    在Xcode中创建故事板文件时,您必须选择是否适用于iPhone或iPad.这意味着应始终将iPhone和iPadUI放入单独的故事板中.这是真的?我的应用程序有多个故事板.虽然Main.storyboard文件在iPhone和iPad之间存在很大差异,但其他故事板几乎完全相同.唯一的区别可能是推动iPhone与iPhone上的popover,可以通过编程方式处理.制作两个故事板似乎非常愚蠢和多余.因此,如果制作一个“通用”故事板,是否应该在Xcode中选择iPhone或iPad?

  6. xcode – 如何通过LLDB命令行添加断点操作?

    如果你从Xcode编辑一个断点,有一个超级有用的选项,可以添加一个“Action”,以便在每次遇到断点时自动执行.如何从LLDB命令行添加此类操作?

  7. XCode:将故事板导出到图像

    我有一个小问题……

  8. xcode6.1 – Xcode 6.1中项目模板中缺少类前缀

    项目模板上曾经有一个类前缀字段,这有助于区分项目类和框架类.Xcode6.1项目模板中不再提供此功能.这背后的意图是什么?

  9. 为Xcode中的iPhone 5手电筒应用调暗LED

    我期待用滑块选项调暗手电筒的LED.我知道Apple支持iOS6但是,我不确定要使用哪些代码.这是我目前在.m文件中的代码.解决方法–(BOOL)setTorchModeOnWithLevel:(float)torchLevel错误:(NSError**)outError你想要的是什么但是,从我所看到的,它只在某些间隔(~0.2)更新.编辑–完整示例:这是一个UiSlider.您需要在滑块上添加I

  10. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部