本文由yake_099(博客)翻譯自raywenderlich,作者:Joshua Greene
原文:How to Use CocoaPods with Swift
最近關(guān)于CocoaPods有很多的議論。你可能從別的開(kāi)發(fā)者那里聽(tīng)到過(guò),或者在Github的目錄中看到過(guò)。如果你之前從來(lái)沒(méi)有用過(guò),你可能會(huì)問(wèn),"CocoaPods到底是什么?"
它不是神秘的亞馬遜區(qū)域的部落人用手撿出來(lái)的生可可的豆莢,肯定不是!讓CocoaPods website來(lái)回答可能是最好的:
CocoaPods是Cocoa項(xiàng)目的依賴(lài)管理工具。它有上千個(gè)能幫你優(yōu)雅地?cái)U(kuò)充自己項(xiàng)目的庫(kù)。
優(yōu)雅地?cái)U(kuò)展項(xiàng)目聽(tīng)起來(lái)很令人好奇,但是什么是依賴(lài)管理工具呢?你為什么會(huì)需要它呢?
無(wú)論你正在創(chuàng)建一個(gè)什么樣的app,你都有可能會(huì)用到別的開(kāi)發(fā)者的代碼,無(wú)論是以框架的形式還是庫(kù)的形式。你可能很熟悉UIKit和Foundation,這兩個(gè)都是蘋(píng)果提供的框架。
在這個(gè)教程中,你將會(huì):
了解為什么要和第三方庫(kù)做朋友
安裝CocoaPods
在一個(gè)starter項(xiàng)目中使用CocoaPods
安裝使用并修改依賴(lài)關(guān)系文件來(lái)提高用戶(hù)體驗(yàn)
了解語(yǔ)義化版本控制
為什么庫(kù)是你的朋友
盡管你并不一定非要使用第三方庫(kù)和框架,但是它們確實(shí)能節(jié)省你很多的時(shí)間,可以讓你集中注意力在優(yōu)化自己的app上,而不是敲無(wú)數(shù)行的你本不需要寫(xiě)的代碼。
你可以不通過(guò)依賴(lài)管理工具來(lái)使用第三方庫(kù)和框架,我們網(wǎng)站提供了關(guān)于這種使用方式的實(shí)踐性的教程。例如,這是我們的 Alamofire教程 和 SwiftyJSON教程。
不使用依賴(lài)管理工具,你可以簡(jiǎn)單地通過(guò)手動(dòng)方式將每個(gè)庫(kù)添加到你的工程里面。然而,這個(gè)方法有幾個(gè)缺點(diǎn):
更新一個(gè)庫(kù)到新的版本可能會(huì)很麻煩,尤其是一個(gè)庫(kù)依賴(lài)于另外一個(gè)庫(kù)的情況,那么就必須將這幾個(gè)庫(kù)全部更新。
在項(xiàng)目中添加三方庫(kù)可能會(huì)需要在代碼中做一些本地的修改,這使得之后更新版本更加困難。
判斷你的app中用到的庫(kù)的當(dāng)前版本也是一件困難的事,尤其是當(dāng)你沒(méi)有提前記錄下來(lái)的時(shí)候。
如果沒(méi)有一個(gè)中央位置來(lái)查看所有可用庫(kù)的話,查找新庫(kù)也是一件很困難的事。
CocoaPods幫你克服以上問(wèn)題甚至更多別的問(wèn)題。它抓取庫(kù)代碼,解決庫(kù)之間的依賴(lài)性問(wèn)題,幫你查找并發(fā)現(xiàn)新的庫(kù),甚至以最簡(jiǎn)便的方式為你的項(xiàng)目配置正確的環(huán)境。
前提
這篇教程要求你熟悉基礎(chǔ)的iOS和Swift開(kāi)發(fā)。如果你完全不了解iOS或者Swift,那么在看這篇教程之前,你最好看下我們網(wǎng)站上一些其他 文章 或者 視頻 教程,然后再回來(lái)看這篇。或者是深入來(lái)學(xué)習(xí)iOS Apprentice。
這篇教程也包含了使用了Core Graphics的一些類(lèi)。了解Core Graphics是一件有利的事,但不是必須的。如果你想學(xué)習(xí)更多關(guān)于Core Graphics的知識(shí),請(qǐng)閱讀我們的 Modern Core Graphics With Swift 系列。
這篇教程要求Xcode 6.3和Swift 1.2.
開(kāi)始
首先你需要安裝CocoaPods。幸運(yùn)的是,CocoaPods被建立在Ruby上,而最近的Mac OS X版本帶有Ruby。這自從OS X 10.7之后就實(shí)現(xiàn)了。
打開(kāi)終端并且輸入以下命令行:
sudo gem install cocoapods
當(dāng)要求的時(shí)候輸入你的密碼。終端的輸出結(jié)果看起來(lái)應(yīng)該是這樣的:
你必須使用sudo來(lái)安裝CocoaPods,但是安裝后就無(wú)需再使用了。
最后,在終端輸入以下命令行來(lái)完成設(shè)置:
pod setup --verbose
這個(gè)過(guò)程可能需要幾分鐘的時(shí)間,因?yàn)樗鼘?nbsp;CocoaPods Master Specs repository 克隆到了你電腦上的~/.cocoapods/目錄下。
verbose選項(xiàng)記錄下了進(jìn)程運(yùn)行時(shí)的進(jìn)展,能讓你看到進(jìn)程而不是一個(gè)僵在那里的屏幕。
太好了,現(xiàn)在你開(kāi)始設(shè)置來(lái)使用CocoaPods了。
代碼時(shí)間!
為客戶(hù)Ice Cream Shop, Inc.開(kāi)發(fā)一款A(yù)pp
你的首要客戶(hù)是Ice Cream Shop, Inc。他們的冰淇淋太受歡迎了以至于不能在柜臺(tái)接收用戶(hù)訂單了。他們雇傭你來(lái)做一個(gè)漂亮的iOS應(yīng)用,那樣就能讓用戶(hù)在他們的iPhone上下訂單了。
你開(kāi)始開(kāi)發(fā)app了,并且進(jìn)展得還不錯(cuò)。在這里 下載開(kāi)始程序 。
打開(kāi)IceCreamShop.xcodeproj,然后運(yùn)行,你會(huì)看到一個(gè)非常好吃的香草冰淇淋。
用戶(hù)應(yīng)該能從這個(gè)屏幕中選擇冰淇淋的口味,但是還不太可能,因?yàn)槟氵€沒(méi)有完成這個(gè)功能。
從Views/Storyboards&Nibs中打開(kāi)Main.storyboard來(lái)看app的布局?,F(xiàn)在我們來(lái)快速整體地看一下這個(gè)app的核心,"選擇你的口味"那一屏:
PickFlavorViewController處理用戶(hù)交互,例如,用戶(hù)選擇了冰淇淋的一種口味。
PickFlavorDataSource是collection view所展示的不同冰淇淋口味的數(shù)據(jù)源。
IceCreamView是一個(gè)自定義的view,可以用來(lái)展示冰淇淋圓錐形的蛋卷,并且它以Flavor model來(lái)支撐。
ScoopCell是一個(gè)自定義的collectionviewcell,它包含了一個(gè)ScoopView,這個(gè)view也是以Flavor model來(lái)支撐的。
由于每個(gè)冰淇淋店一般都會(huì)有一些招牌口味,每個(gè)也有他們本地的風(fēng)味。因此,F(xiàn)lavor的實(shí)例中包含的數(shù)據(jù)需要通過(guò)web service來(lái)提供。
然而,這還沒(méi)有回答問(wèn)題,"為什么用戶(hù)不能選擇一個(gè)冰淇淋口味?"
在Controllers這個(gè)分組下面,打開(kāi)PickFlavorViewController.swift,你會(huì)看到一個(gè)備用的方法:
private func loadFlavors() { // Implement this }
哈哈,這里面沒(méi)有口味,你需要實(shí)現(xiàn)它。
你可以使用NSURLConnection或 NSURLSession 并用你自己寫(xiě)的網(wǎng)絡(luò)類(lèi),這兒還有一個(gè)更簡(jiǎn)單的辦法:Alamofire,一個(gè)開(kāi)源的網(wǎng)絡(luò)庫(kù)。
你可能想就這么把它下載下來(lái)并將文件拖拽到工程里面。然而,那是很麻煩的方法。CocoaPods提供了更加優(yōu)雅和靈活的解決方法。
所以,閑話少說(shuō)...
安裝你的第一個(gè)依賴(lài)管理工具
首先你要關(guān)掉Xcode
是的,你說(shuō)的對(duì),該創(chuàng)建Podfile了,在那兒你要定義工程的依賴(lài)管理。
打開(kāi)終端,用 cd 命令進(jìn)入包含你IceCreamShop項(xiàng)目的那個(gè)目錄下:
cd ~/Path/To/Folder/Containing/IceCreamShop接下來(lái),輸入下面的命令:
pod init
這將為你的項(xiàng)目創(chuàng)建一個(gè) PodFile
輸入下面的命令行打開(kāi)PodFile,并使用Xcode進(jìn)行編輯:
open -a Xcode Podfile
注意:你不能使用TextEdit來(lái)編輯Podfile,因?yàn)樗锌赡苡脠D形化的更有吸引力的typeset quotes代替standard quotes,這可能導(dǎo)致CocoaPods不能理解并拋出錯(cuò)誤,所以最好用Xcode或者別的編程文本編輯器來(lái)編輯你的Podfile。
默認(rèn)的podFile看起來(lái)是這樣的:
# Uncomment this line to define a global platform for your project # platform :ios, '6.0' target 'IceCreamShop' do end target 'IceCreamShopTests' do end
將注釋的內(nèi)容替換成下面的兩行:
platform :ios, "8.0" use_frameworks!
這就告訴了CocoaPods--你的項(xiàng)目使用的是iOS 8.0,并且將使用框架來(lái)代替靜態(tài)庫(kù)。
想要在Swift中使用CocoaPods,你必須明確的寫(xiě)出use_frameworks! 來(lái)選擇使用框架。如果你忘了寫(xiě)這個(gè),CocoaPods能檢測(cè)到你使用使用Swift CocoaPods,你安裝pods的時(shí)候就會(huì)報(bào)錯(cuò)。
如果你僅僅使用過(guò)Swift編程,這可能看起來(lái)有些奇怪--那是因?yàn)镻odfiel實(shí)際上使用Ruby寫(xiě)的。你無(wú)需為了使用CocoaPods而學(xué)習(xí)Ruby,但是你要知道即使是很小的文字錯(cuò)誤通常也會(huì)使CocoaPods出錯(cuò)。
關(guān)于“庫(kù)”
你會(huì)看到"library"通常代指庫(kù)或框架。很抱歉這個(gè)教程也在無(wú)意間混雜了這兩個(gè)概念。實(shí)際上,當(dāng)某人提到"Swift library",他們實(shí)際上指的是"Swift dynamic frameworks",因?yàn)閟wift不允許靜態(tài)庫(kù)。
你可能想知道,"庫(kù)(library)和框架(frameworkd)和cocoaPod之間的不同之處"。
Cocoapod或者簡(jiǎn)寫(xiě)為"pod"是一種慣常的叫法,用來(lái)表示使用Cocoapods工具添加庫(kù)或者框架到你的應(yīng)用程序中。
iOS 8引入了動(dòng)態(tài)框架,這就允許了代碼、圖片和其他的東西(assets)可以被一起打包。在iOS 8之前,CocoaPods被作為靜態(tài)庫(kù)來(lái)創(chuàng)建,就是很"臃腫的"二進(jìn)制文件。這意味著它包含了一些代碼說(shuō)明設(shè)置(例如i386 for the simulator, armv7 for devices等),但是它們不允許包含任何資源,例如圖片或資產(chǎn)。
另外一個(gè)重要的區(qū)別是動(dòng)態(tài)框架有命名空間類(lèi),而靜態(tài)庫(kù)沒(méi)有。所以,如果單個(gè)項(xiàng)目中不同的靜態(tài)庫(kù)里分別有一個(gè)叫做MyTestClasses的類(lèi),那么Xcode可能不能成功創(chuàng)建工程因?yàn)樗鼤?huì)因重復(fù)的標(biāo)識(shí)而導(dǎo)致連接失敗。然而,Xcode很樂(lè)意在一個(gè)工程里的不同框架下分別有一個(gè)名字相同的類(lèi)。
為什么這很重要?不像OC,標(biāo)準(zhǔn)的Swift運(yùn)行時(shí)庫(kù)(runtime libraries)沒(méi)有包含在iOS中!這意味著你的框架必須包含了必要的Swift運(yùn)行時(shí)庫(kù)。因此,用Swift語(yǔ)言寫(xiě)的pods必須以動(dòng)態(tài)框架形式創(chuàng)建。如果蘋(píng)果允許Swift靜態(tài)庫(kù),那么使用同一個(gè)標(biāo)準(zhǔn)運(yùn)行時(shí)依賴(lài)的不同庫(kù)中將會(huì)產(chǎn)生重復(fù)符號(hào)。
幸運(yùn)的是,CocoaPods為你做了這些事。它甚至能做到一次性包含需要的依賴(lài)關(guān)系。你所要做的就是在使用swift cocoaPods時(shí)記得在Podfile中包含use_frameworks! ,那就沒(méi)問(wèn)題了。
amazing,對(duì)不對(duì)?
安裝你的第一個(gè)依賴(lài)關(guān)系
這是使用cocoapods安裝你的依賴(lài)性文件的最后的時(shí)刻。將下面的內(nèi)容添加到你的Podfile里面
pod 'Alamofire', '1.2.3'
這個(gè)是告訴CocoaPods你想添加1.1.4版本的Alamofire到你的工程中作為一個(gè)依賴(lài)性文件,這是寫(xiě)這篇教程時(shí)的最新版本。
保存并關(guān)閉podFile
現(xiàn)在你需要告訴Cocoapods為你的工程安裝依賴(lài)性文件。在終端中輸入下面的命令,但是要首先確認(rèn)你還在包含了IceCreamShop和Podfile的目錄下:
pod install
你會(huì)看到輸出結(jié)果與下面的類(lèi)似
Analyzing dependencies Downloading dependencies Installing Alamofire (1.1.4) Generating Pods project Integrating client project [!] Please close any current Xcode sessions and use `IceCreamShop.xcworkspace` for this project from now on.
使用Finder打開(kāi)你的工程文件夾,你看看到CocoaPods創(chuàng)建了一個(gè)新的IceCreamShop.xcworkspace文件和一個(gè)Pods文件來(lái)存儲(chǔ)所有的工程依賴(lài)文件。
注意:從現(xiàn)在開(kāi)始,就像命令行警告提示的,你必須使用workspace文件而不是project,否則你將遇到編譯錯(cuò)誤。
太棒了!你已經(jīng)使用CocoaPods添加了你的第一個(gè)依賴(lài)管理。
使用安裝好的Pods
如果已經(jīng)打開(kāi)了Xcode工程,那要關(guān)閉它并打開(kāi)IceCream.xcworkspace.
打開(kāi)PickFlavorViewController.swift,在現(xiàn)有的導(dǎo)入下添加以下代碼:
import Alamofire
點(diǎn)擊command+b進(jìn)行編譯。如果一切都沒(méi)有問(wèn)題,你不會(huì)收到任何編譯錯(cuò)誤的。
接下來(lái),用下面的代碼代替loadFlavors()方法
private func loadFlavors() { // 1 Alamofire.request( .GET, "http://www.raywenderlich.com/downloads/Flavors.plist", parameters: nil, encoding: .PropertyList(.XMLFormat_v1_0, 0), headers: nil) .responsePropertyList { [weak self] (_, _, result) -> Void in // 2 guard let strongSelf = self else { return } var flavorsArray: [[String : String]]! = nil // 3 switch result { case .Success(let array): if let array = array as? [[String : String]] { flavorsArray = array } case .Failure(_, _): print("Couldn't download flavors!") return } // 4 strongSelf.flavors = strongSelf.flavorFactory.flavorsFromDictionaryArray(flavorsArray) strongSelf.collectionView.reloadData() strongSelf.selectFirstFlavor() }; }
下面是對(duì)這個(gè)過(guò)程的詳細(xì)描述:
1.你用Alamofire創(chuàng)建了一個(gè)get請(qǐng)求,并下載了一個(gè)包含冰淇淋口味的plist文件。
2.一般你去看查看請(qǐng)求是否出錯(cuò)并解析錯(cuò)誤?,F(xiàn)在如果出錯(cuò)的話你只需要打印錯(cuò)誤。
3.將一個(gè)AnyObject 類(lèi)型數(shù)組轉(zhuǎn)換為字典數(shù)組。
4.如果數(shù)組是空的,你只需要打印一個(gè)錯(cuò)誤信息。
5.如果一切都OK的話,你就將FlavorFactory方法創(chuàng)建的包含F(xiàn)lavor對(duì)象的數(shù)組賦值給self.flavors。這是一個(gè)同事為你寫(xiě)的類(lèi),是用來(lái)將裝了字典類(lèi)型對(duì)象的數(shù)組轉(zhuǎn)換為Flavor對(duì)象。如果你喜歡的話你可以仔細(xì)看看這個(gè)工廠類(lèi),但是它對(duì)教程的其他部分不是特別重要。
編譯并運(yùn)行!現(xiàn)在你可以選擇一個(gè)冰淇淋口味了!
選擇裝飾物
app看起來(lái)很不錯(cuò)了,但是你仍然可以進(jìn)行優(yōu)化。
你有沒(méi)有注意到app花費(fèi)了幾秒鐘的時(shí)間來(lái)下載flavors文件?如果你的網(wǎng)絡(luò)很快的話你可能注意不到,但是用戶(hù)不會(huì)都這么幸運(yùn)的。
為了讓用戶(hù)理解app現(xiàn)在正在加載什么內(nèi)容,你可以顯示一個(gè)加載指示器。MBProgressHUD 是個(gè)很不錯(cuò)的加載指示器.并且它支持CocoaPods,多么的巧合!
你需要把這個(gè)加入到你的podfile里面。你現(xiàn)在不用通過(guò)命令行打開(kāi)podfile了,你可以在工作區(qū)的pods中找到它。
打開(kāi)podFile并在Alamofire后面加入下面幾行:
pod 'MBProgressHUD', '~> 0.9.0'
保存文件,并在終端通過(guò)pod install命令來(lái)安裝依賴(lài)文件,就像你之前做的那樣。
這次發(fā)現(xiàn)什么不同的了嗎?對(duì),你寫(xiě)出了具體的版本號(hào)~>0.9.0。這兒會(huì)發(fā)生什么呢?
Cocoapods建議所有的pods都使用語(yǔ)義化版本號(hào)(Semantic Versioning)。
語(yǔ)義化版本號(hào)
這三個(gè)數(shù)字被定義為主要的,次要的和補(bǔ)丁版本號(hào)。例如,版本號(hào)0.9.0會(huì)被翻譯為
當(dāng)主要的(major)版本號(hào)數(shù)字增加時(shí), 意味著你做了一些不能兼容舊版本的更新。當(dāng)你將pod升級(jí)到下一個(gè)主要版本時(shí),你可能需要修復(fù)編譯錯(cuò)誤,否則pod可能跟之前表現(xiàn)得不太一樣。
當(dāng)次要(minor)版本號(hào)增加時(shí),意味著增加了新功能,但同時(shí)兼容舊版本。當(dāng)你決定升級(jí)時(shí),你可能需要也可能不需要新的功能,但是它不應(yīng)該引起編譯錯(cuò)誤或者改變現(xiàn)有的功能。
當(dāng)補(bǔ)?。╬atch)版本號(hào)增加的時(shí)候,這意味著做了bug修復(fù)。但是沒(méi)有沒(méi)有增加也沒(méi)有改變功能。一般來(lái)說(shuō),你會(huì)希望盡快更新補(bǔ)丁版本到最新的版本,以便使用最新最穩(wěn)定版本的pod。
最后,最高版本號(hào)(major>minor>patch)必須按照以上規(guī)則逐步增加,而較低的版本號(hào)必須從0開(kāi)始。
需要一個(gè)例子嗎?
考慮一個(gè)當(dāng)前版本為1.2.3的pod。
如果做了一些不能向后兼容的改變,沒(méi)有新的功能,但是修改了現(xiàn)有的bug,那么下一個(gè)版本是2.0.0
挑戰(zhàn)時(shí)間
1.如果一個(gè)pod當(dāng)前版本號(hào)是2.4.6,并且做了一些修復(fù)bug的改變,添加了一些向后兼容的功能,新版本應(yīng)該是多少呢?
答案:2.5.0
解釋?zhuān)喝绻薷陌蚝蠹嫒莸男鹿δ?,次要版本?hào)(monir)就要增加了,并且補(bǔ)丁版本號(hào)(patch)就要被置為0.
2.如果一個(gè)pod的當(dāng)前版本是3.5.8,并且對(duì)當(dāng)前的功能做了一些改變,新版本應(yīng)該是多少?
答案:4.0.0
解釋?zhuān)?/span>如果改變修改了現(xiàn)有的功能,那么這就是不向后兼容的。所以,主要版本號(hào)(major)必須增加,而次要版本和補(bǔ)充版本置為0.
3.如果一個(gè)pod的向前版本號(hào)是10.20.30并且只修復(fù)了一些bug,那么新版本號(hào)應(yīng)該是多少?
答案:10.20.31
解釋?zhuān)?/span>如果只修復(fù)了bug,補(bǔ)充版本號(hào)(patch)就要增加了。
說(shuō)了這么多,這兒有一個(gè)例外:
如果一個(gè)pod的版本低于1.0.0,這就被認(rèn)為是測(cè)試版,次要版本號(hào)(minor)增加可能意味著向后兼容的改變。
所以在MBProgressHUB中使用~>0.9.0意味著你需要安裝大于或者等于0.9.0但是小于0.10.0的最新的版本.
這就保證了你在安裝pod的時(shí)候獲得了最新的bug修復(fù),但是不會(huì)意外的拉去向后兼容的改變。這兒還有一些其他的可用操作。具體的列表,請(qǐng)看Podfile Syntax Reference。
顯示進(jìn)程
現(xiàn)在,回到PickFlavorViewController.swift,在其他的引用下面添加下面的內(nèi)容
import MBProgressHUD
下一步,在loadFlavors()后面加入以下方法
private func showLoadingHUD() { let hud = MBProgressHUD.showHUDAddedTo(contentView, animated: true) hud.labelText = "Loading..." } private func hideLoadingHUD() { MBProgressHUD.hideAllHUDsForView(contentView, animated: true) }
現(xiàn)在在loadFlavors()方法中,添加下面代碼:
private func loadFlavors() { showLoadingHUD() // <-- Add this line // 1 Alamofire.request( .GET, "http://www.raywenderlich.com/downloads/Flavors.plist", parameters: nil, encoding: .PropertyList(.XMLFormat_v1_0, 0), headers: nil) .responsePropertyList { [weak self] (_, _, result) -> Void in // 2 guard let strongSelf = self else { return } strongSelf.hideLoadingHUD() // <-- Add this line // ...
就像方法名字所表明的,showLoadingHUD()方法會(huì)在GET請(qǐng)求下載時(shí)產(chǎn)生一個(gè)MBProgressHUD的實(shí)例對(duì)象,當(dāng)請(qǐng)求結(jié)束時(shí)hideLoadingHUD()會(huì)將HUD隱藏。由于showLoadingHUD()在閉包之外所以它不需要前置代碼self。
編譯運(yùn)行?,F(xiàn)在你會(huì)看到一個(gè)加載指示器:
干的漂亮!用戶(hù)現(xiàn)在可以選擇他們最喜歡的冰淇淋口味了,并且在口味文件下載的時(shí)候會(huì)顯示一個(gè)正在加載的指示器。
下一步
你可以從這兒 下載完整的項(xiàng)目
恭喜你,現(xiàn)在你已經(jīng)學(xué)會(huì)了使用cocoapods的基礎(chǔ),包括創(chuàng)建和修改依賴(lài)性文件,并且理解了語(yǔ)義化版本號(hào)?,F(xiàn)在你可以準(zhǔn)備開(kāi)始在你自己的項(xiàng)目中使用他們了。
當(dāng)然你 還可以用cocoapods做很多別的事。你可以在Cocoapods官方網(wǎng)站上搜索現(xiàn)有的pods。也可以參考Cocoapods指南去學(xué)習(xí)如何使用這個(gè)杰出工具的更多細(xì)節(jié)內(nèi)容。但是要記住,一旦你開(kāi)始使用你會(huì)好奇你曾經(jīng)是怎么不用它來(lái)進(jìn)行管理的!
希望你享受閱讀這篇教程的過(guò)程我寫(xiě)的時(shí)候。
來(lái)源:Ray Wenderlich