三方依赖管理工具使用(一) —— Carthage的使用(一)

版本记录

版本号 时间
V1.0 2020.03.23 星期一

前言

在工程中,我们总要使用各种第三方依赖管理工具,方便我们进行管理。比如Cocoapods和Carthage等,这个模块我们就一起看一下这些第三方依赖管理工具。

开始

首先看下主要内容:

在本教程中,您将了解什么是Carthage,如何安装它,以及如何使用它来声明、安装和集成您的依赖项。

下面看下写作环境

Swift 5, iOS 13, Xcode 11

iOS开发的一大优点是可以使用广泛的第三方库。如果您尝试过其中的一些库,您就会知道使用其他人的代码而不是重新发明轮子的价值。

有这么多可用的库,管理依赖关系可能会变得很棘手。这就是依赖管理器(dependency managers)的用武之地。

Carthage是一个简单的macOSiOS依赖管理器,由GitHub的一群开发人员创建。

Carthage不仅是第一个与Swift一起工作的依赖管理器,而且它也是用Swift写的!它只使用动态框架,而不是静态库。

在本教程中,您将:

  • 了解为什么以及何时使用依赖项管理器,以及Carthage与其他工具的不同之处。
  • 安装Carthage
  • 声明依赖项,在项目中安装和集成它们。
  • 将依赖项升级到不同的版本。

您将通过构建一个应用程序来实现这一点,该应用程序使用DuckDuckGo API为搜索词提供定义。

打开初始项目,它包括DuckDuckDefine的基本框架,这是一个使用DuckDuckGo API查找定义和图像的简单工具。有一个问题:它还没有执行任何搜索!

Xcode中打开DuckDuckDefine.xcodeproj。注意两个视图控制器:SearchViewController为用户执行搜索提供了一个搜索栏,DefinitionViewController显示搜索项的定义。

这个操作的中心是在DuckDuckGo.swift的——好吧,等你完成的时候他们就会敏捷了!目前,performSearch(for:completion:)是一个懒惰的、无用的代码块。

要执行搜索并显示结果,您需要做两件事:

  • 使用DuckDuckGo API进行查询。
  • 显示检索到的单词的图像。

有许多开源库可以帮助完成这两项任务。Alamofire是一个很棒的Swift库,可以简化web请求。AlamofireImage使处理图像在Swift更愉快。

你猜怎么着?您将使用Carthage将这两个依赖项都添加到您的项目中。


Advantages of Dependency Management

要将AlamofireAlamofireImage添加到您的项目中,您可以访问它们各自的GitHub页面,下载包含它们的源代码的zip文件并将它们放入您的项目中。为什么要用Carthage这样的工具呢?

依赖管理器执行一些方便的函数,包括:

  • 简化和标准化获取第三方代码并将其合并到您的项目中的过程。如果没有这个工具,您可以通过手动复制源代码文件、放入预编译的二进制文件或使用诸如Git子模块之类的机制来实现这一点。
  • 使将来更新第三方库更加容易。想象一下,必须访问每个依赖项的GitHub页面,下载源代码,并在每次更新时将其放入项目中。你为什么要这样对自己?
  • 为您使用的每个依赖项选择适当的和兼容的版本。例如,当依赖项彼此依赖或共享另一个依赖项时,手动添加依赖项可能会很棘手。

大多数依赖关系管理器构建项目依赖关系及其子依赖关系的依赖关系图dependency graph,然后确定每个依赖关系的最佳版本。

你可以手工做同样的事情,依赖项管理器更简单,更不容易出错。


Comparing Dependency Managers

最流行的依赖项管理器之一是CocoaPods,它简化了将库集成到项目中的过程。它在iOS社区中被广泛使用。

苹果提供了自己的依赖管理器,叫做Swift包管理器Swift Package Manager,用于在Swift 3.0及以上版本中共享和分发包。

虽然CocoaPodsSwift Package Manager是很棒的工具,但Carthage提供了一个更简单、更实用的选择。那么哪一个更适合你呢?在下一节中,您将比较Carthage与其他一些流行的工具。

1. Carthage Versus CocoaPods

CarthageCocoaPods有何不同?除了最受欢迎的iOS依赖管理器,你还会使用其他工具吗?

虽然CocoaPods很容易使用,但并不简单。Carthage背后的哲学是依赖管理器应该是简单的。

CocoaPods增加了应用程序开发和库发布流程的复杂性:

  • 库必须创建、更新和托管Podspec文件。如果应用程序开发人员希望使用的库不存在,他们必须编写自己的库。
  • 当将pod添加到项目中时,CocoaPods创建一个新的Xcode项目,为每个单独的pod创建一个目标,并添加一个包含它们的工作空间。您必须使用工作空间,并相信CocoaPods项目是有效的。您还必须维护那些额外的构建设置。
  • CocoaPods使用一个集中的Podspecs存储库。这可能会消失或变得无法访问,从而导致问题。

Carthage的目标是提供一种更简单、更灵活、更容易理解和维护的工具。

Carthage是这样做到的:

  • 它不会改变您的Xcode项目,也不会强迫您使用工作空间。
  • 您不需要podspec或一个集中式的存储库来存放库作者提交的pod。如果您可以将项目构建为框架,那么可以将其与Carthage一起使用,Carthage直接利用来自GitXcode的现有信息。
  • Carthage并没有什么神奇的事;你总是在掌控一切。您将依赖项添加到Xcode项目中,然后Carthage获取并构建它们。

注意:Carthage使用动态库来实现其简单性。这意味着你的项目必须支持iOS 8或更高版本。

2. Carthage Versus Swift Package Manager

那么CarthageSwift Package Manager有什么区别呢?

Swift Package Manager的主要关注点是以一种对开发者友好的方式共享Swift代码。Carthage的重点是共享动态库。动态库是Swift包的超集。

包是Swift源文件和manifest文件的集合。清单文件定义包的名称及其内容。一个包包含Swift代码、Objective-C代码、图像等非代码资产或这三者的任何组合。

SPM有很多优点,特别是Xcode 11内置了对SPM包的支持。然而,在撰写本文时,SPM有一些限制,使许多库无法支持它。例如,它不支持共享已经构建的二进制文件,只支持包的源代码。如果你想要一个闭源库,你不能使用SPM。此外,SPM支持向框架添加源代码,但不支持向图像或其他数据文件等资源添加源代码。

这意味着您可能使用的许多库都不会很快获得SPM支持。现在,您仍然需要依赖Carthage或CocoaPods来实现这些库。


Installing Carthage

现在你已经获得了一些背景知识,是时候学习一下Carthage是多么的简单了!

Carthage的核心是一个命令行工具,它可以帮助获取和构建依赖项。

有两种方式来安装这个工具:

  • 下载并运行最新版本的.pkg安装程序。
  • 使用Homebrew软件包管理器。

就像Carthage帮助安装用于Cocoa开发的包一样,Homebrew帮助安装用于macOS的有用的Unix工具。

出于本教程的目的,您将使用.pkg安装程序。

GitHub下载Carthage的最新版本。然后,在Assets下,选择Carthage.pkg

双击Carthage.pkg来运行安装程序。单击Continue,选择要安装到的位置,再次单击Continue,最后单击Install

注意:当您尝试运行安装程序时,您可能会看到一条消息:“Carthage.pkg can’t be opened because it is from an unidentified developer.”。如果是,Control-click安装程序,并从上下文菜单中选择Open

已经完成了,要检查Carthage是否正确安装,请打开终端并运行以下命令:

carthage version

这将显示您安装的Carthage版本。

接下来,您需要告诉Carthage使用Cartfile安装哪些库。


Creating Your First Cartfile

Cartfile是一个简单的文本文件,它描述了您的项目对Carthage的依赖关系,因此它可以决定安装什么。Cartfile中的每一行都指明了在哪里获取一个依赖项,依赖项的名称,以及使用哪个版本。Cartfile相当于CocoaPods Podfile

要创建第一个文件,请转到Terminal,然后使用cd命令导航到项目的根目录(包含.xcodeproj文件的目录):

cd ~/Path/To/Starter/Project

使用touch命令创建一个空的Cartfile文件

touch Cartfile

然后在Xcode中打开文件进行编辑:

open -a Xcode Cartfile

如果您熟悉另一个文本编辑器,比如Vim,那么可以随意使用它。但是,不要使用TextEdit来编辑文件。在TextEdit中,很容易不小心使用“聪明的引号”而不是直接的引号,这会使Carthage出现识别问题。

添加以下行到Cartfile并保存:

github "Alamofire/Alamofire" == 4.9.0
github "Alamofire/AlamofireImage" ~> 3.4

这两行代码告诉Carthage,您的项目需要Alamofire版本4.9.0和与版本3.4兼容的AlamofireImage的最新版本。


The Cartfile Format

您可以在OGDL:Ordered Graph Data Language的子集中编写cartfile。这听起来很神奇,但其实很简单。在Cartfile的每一行都有两个关键信息:

  • Dependency origin:这告诉Carthage在哪里获取依赖项。Carthage支持两种起源:

    • githubgithub用于github托管的项目(线索在名字里!)您可以使用Username/ProjectName格式指定GitHub项目,就像您在上面的Cartfile中所做的那样。
    • git:用于托管在其他地方的通用git存储库。可以使用git关键字,然后是git存储库的路径,不管是使用git://、http://或ssh://的远程URL,还是开发机器上git存储库的本地路径。
  • Dependency version:在这里,你告诉Carthage你想使用哪个依赖版本。根据你想要的具体程度,你可以有几种选择:

    • == 1.0:表示“完全使用1.0版本”。
    • >= 1.0:表示“使用1.0或更高版本”。
    • ~> 1.0:翻译过来是“使用与1.0兼容的任何版本”,意思是直到下一个主要版本的任何版本。
    • Branch name / tag name / commit name:意思是“使用这个特定的git分支/标签/提交”。例如,您可以指定master,或者像5c8a74a这样的提交hash值。

以下是一些例子:

如果你指定了~> 1.7.5Carthage认为从1.7.52.0的任何版本都是兼容的,但不包括2.0

同样,如果您指定~> 2.0,那么Carthage将使用2.0版或任何后续版本,但不包括3.0或以上版本。

Carthage使用语义版本semantic versioning控制来确定兼容性。

如果您没有指定一个版本,Carthage将使用与您的其他依赖项兼容的最新版本。你可以在Carthage的自述文件 Carthage’s README file中看到这些选项的例子。


Building Dependencies

现在您已经有了一个Cartfile,是时候使用它并安装一些依赖项了!

注意:这个Carthage教程使用的是Swift 5。在撰写本文时,Swift 5仅在Xcode 11中可用。确保你已经配置你的命令行工具使用Xcode 11从终端运行以下命令:

sudo xcode-select -s <path to Xcode 11>/Xcode.app/Contents/Developer 

请确保将path to Xcode 11替换为您的机器到Xcode 11的特定路径。

Xcode中关闭你的Cartfile,返回Terminal。运行以下命令:

carthage update --platform iOS

这指示CarthageCartfile中克隆Git存储库,然后将每个依赖项构建到框架中。您将看到显示结果的输出,如下所示:

*** Cloning AlamofireImage
*** Cloning Alamofire
*** Checking out Alamofire at "4.9.0"
*** Checking out AlamofireImage at "3.6.0"
*** xcodebuild output can be found in /var/folders/bj/3hftn5nn0qlfrs2tqrydgjc80000gn/T/carthage-xcodebuild.7MbtQO.log
*** Building scheme "Alamofire iOS" in Alamofire.xcworkspace
*** Building scheme "AlamofireImage iOS" in AlamofireImage.xcworkspace

--platform iOS确保Carthage只构建iOS框架。如果你不指定一个平台,Carthage将为所有平台构建框架——通常是MaciOS——由库支持。

如果您想查看更多选项,请运行carthage help update

默认情况下,Carthage执行它的checkout并构建一个名为Carthage的新目录,您将在与Cartfile相同的位置找到它。现在打开这个目录运行:

open Carthage

您将看到出现一个Finder窗口,其中包含两个目录:Buildcheckout。花点时间看看Carthage为你创造了什么。


Building Artifacts

当您使用CocoaPods时,它会对Xcode项目进行几处更改,并将结果与一个特殊的Pods项目绑定到Xcode工作空间中。

Carthage有点不同。它检查依赖项的代码并将结果构建到二进制框架中。然后由您来将框架集成到您的项目中。

这听起来像是额外的工作,但它是有益的。只需几个步骤,您就可以更清楚地了解项目的变化。

当你运行carthage update时,Carthage会为你创建两个文件和目录:

  • Cartfile.resolved:此文件作为Cartfile的伙伴。它精确地定义了Carthage选择安装的依赖项版本。强烈建议将此文件提交到版本控制存储库。它的存在确保了其他开发人员可以通过使用完全相同的依赖项版本快速起步。
  • Carthage目录,包含两个子目录:
    • Build:它包含为每个依赖项构建的框架。您可以将这些集成到您的项目中,并且很快就会这样做。Carthage要么从源代码构建每个框架,要么从GitHub上的项目发布页面下载框架。
    • Checkouts:这是Carthage签出每个准备构建到框架中的依赖项的源代码的地方。Carthage维护它自己的内部依赖存储库缓存,因此它不必为不同的项目多次克隆相同的源。

1. Avoiding Problems With Your Artifacts

您是否将BuildCheckouts文件夹提交到版本控制存储库取决于您自己。这不是必需的,但是这样做意味着任何克隆您的存储库的人都将拥有每个可用依赖项的二进制文件和源代码。

如果GitHub不可用,或者源存储库被删除,有这个备份可能是一个有用的保险策略。

不要更改Checkouts文件夹中的任何代码,因为将来的carthage updatecarthage checkout命令可能会在任何时候覆盖其内容。你的努力工作一眨眼就会消失。

如果必须修改依赖项,请使--use-submodules选项运行carthage update

通过这个选项,CarthageCheckouts文件夹中的每个依赖项作为子模块添加到Git存储库中,这意味着您可以更改依赖项的源代码,并将这些更改提交到其他地方,而不必担心覆盖。

注意:如果其他开发人员使用您的项目,而您的代码还没有提交built框架,那么他们需要在检查完您的项目后运行carthage bootstrap

bootstrap命令下载并构建在Cartfile.resolved中指定的依赖项的精确版本。carthage update将更新项目以使用每个依赖项的最新兼容版本,这可能是不可取的。

现在,如何实际使用这些您努力创建的构建工件呢?您将在下一节中完成这项工作。


Adding Frameworks to Your Project

回到Xcode,在项目导航器中单击DuckDuckDefine项目。选择DuckDuckDefine目标。选择顶部的General选项卡,然后滚动到底部的Frameworks, Libraries, and Embedded Content部分。

Carthage Finder窗口中,导航到Build/iOS。将Alamofire.frameworkAlamofireImage.framework拖到Xcode中的链接框架和库部分:

这告诉Xcode将您的应用程序链接到这些框架,允许您在代码中使用它们。

接下来,切换到Build Phases。单击编辑器左上方的+图标,并选择New Run Script Phase。在Run Script下的代码块中添加以下命令:

/usr/local/bin/carthage copy-frameworks

点击Input Files下的+图标,为每个框架添加一个条目:

$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
$(SRCROOT)/Carthage/Build/iOS/AlamofireImage.framework

结果如下所示:

严格地说,这个构建阶段并不是您的项目运行所必需的。然而,对于一个App Store submission bug来说,这是一个巧妙的解决方案。在这个bug中,带有iOS模拟器二进制图像框架的应用程序会被自动拒绝。

carthage copy-frameworks命令去掉了这些额外的体系架构。

尽管现在还没有什么新的东西可以看到,但是构建和运行以确保一切都如预期的那样工作。当应用启动时,你会看到搜索视图控制器。

好了。一切看起来不错。接下来,您将致力于升级依赖项。


Upgrading Frameworks

我要坦白一件事。

还记得你之前创建你的Cartfile时,我告诉过你应该安装哪个版本的AlamofireAlamofireImage吗?我给了你坏消息。我告诉过你用旧版本的Alamofire

别生气!我做这件事是出于好意。将此视为学习如何升级依赖项的机会。

再次打开你的Cartfile。从你的项目的目录在终端,运行:

open -a Xcode Cartfile

改变Alamofire那行

github "Alamofire/Alamofire" ~> 4.9.0

如前所述,此代码指示Carthage使用与4.9.0兼容的任何版本的Alamofire。这包括任何版本,但不包括未来的5.0版本。

在添加与Carthage的依赖项时,您希望考虑兼容性并限制目标版本。这样,您就知道它的API和功能的确切状态。

例如,一个依赖项的5.0版本可能包括破坏应用程序的API更改。如果您使用4.9.0构建项目,就不会希望自动升级到该版本。

保存并关闭Cartfile并返回到终端。执行另一个更新:

carthage update --platform iOS

这告诉Carthage去寻找每个依赖项的更新版本。然后,如果需要,它将检查并构建它们。在本例中,它获取最新版本的Alamofire

因为您的项目已经包含了对Alamofire构建的.framework的引用,并且Carthage在磁盘上的相同位置重新构建了新版本,所以您可以坐下来让Carthage来做这项工作。您的项目将自动使用最新版本的Alamofire!


Duck, Duck… GO!

现在您已经将AlamofireAlamofireImage集成到项目中,您可以通过执行一些web搜索来使用它们。

Xcode中,打开DuckDuckGo.swift。在文件的顶部,添加下面的导入

import Alamofire

下面,用下面替换performSearch(for:completion:)

// 1
let parameters: Parameters = [
  "q": term,
  "format": "json",
  "pretty": 1,
  "no_html": 1,
  "skip_disambig": 1
]

// 2
Alamofire.request(
  "https://api.duckduckgo.com",
  method: .get,
  parameters: parameters)
  .responseData { response in
    // 3
    guard 
      response.result.isSuccess, 
      let jsonData = response.result.value 
      else {
        completion(nil)
        return
    }
    
    // 4
    let decoder = JSONDecoder()
    
    guard 
      let definition = try? decoder.decode(Definition.self, from: jsonData),
      definition.resultType == .article 
      else {
        completion(nil)
        return
    }
    
    completion(definition)
}

它的作用如下:

  • 1) 首先,构建要发送给DuckDuckGo的参数。其中最重要的两个是q(搜索项本身)和format(格式),后者告诉web服务使用JSON响应。
  • 2) 然后使用Alamofire执行请求。这个调用使用上面创建的参数字典向https://api.duckduckgo.com发出一个GET请求。
  • 3) 一旦响应返回,检查请求是否失败,并绑定JSON响应对象以确保它有一个值。如果出了问题,尽早退出。
  • 4) 接下来,使用JSONDecoder对定义进行反序列化,这遵循Codable协议。DuckDuckGo API可以返回一系列不同的结果类型,但是这里介绍的是Article,它提供了搜索词的简单定义。筛选文章,然后将检索到的定义传递给完成处理程序。

注意:如果你想知道为什么skip_disambig参数存在,它是告诉DuckDuckGo不要返回disambiguation结果。

消除歧义的结果就像维基百科上克里斯·埃文斯this Christopher Evans page on Wikipedia的页面一样,需要说明的是无论你指的是电影演员克里斯·埃文斯,英国电视名人克里斯·埃文斯还是火车劫匪克里斯·埃文斯。

skip_disambig意味着API将选择最可能的结果并返回它。

构建和运行。一旦应用程序启动,在搜索栏中输入“Duck”。您将在下一个屏幕上看到定义。

然而,少了一样东西:一张照片!读鸭子是一回事,但谁还会读呢?照片是有价值的——好吧,我就不啰嗦了,你知道我的意思。

打开DefinitionViewController.swift并在顶部现有的UIKit导入下面添加一个新的导入:

import AlamofireImage

viewDidLoad下面添加下面代码:

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
    
  if let imageURL = definition.imageURL {
    imageView.af_setImage(withURL: imageURL) { _ in
      self.activityIndicatorView.stopAnimating()
    }
  }
}

af_setImageAlamofireImage提供的UIImageView的扩展。您可以调用它来检索在定义imageURL中找到的图像。检索后,活动指示器的动画将停止。

构建并运行,然后再次执行搜索。

如果你想更多地了解Carthage,你的第一站应该是 Carthage README和关于Build Artifacts的文档。

最后,如果你想了解更多关于Swift Package Manager的信息。

后记

本篇主要讲述了Carthage的使用,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容