如何在 Swift 中使用 CocoaPods
本文介紹如何在 Swift 項目中使用 CocoaPods 。如果你已經(jīng)精通 Bridging Header 的方法,請直接跳到 “擴展 CocoaPods” 一節(jié)。
什么是 CocoaPods
CocoaPods is the dependency manager for Objective-C projects. It has thousands of libraries and can help you scale your projects elegantly.
從介紹看,它是主要給 Objective-C 項目用的,但是我們可以很容易地混合 Objective-C 和 Swift 到同個項目,從而利用大量的 CocoaPods 庫和 Swift 漂亮舒服的語法。
作為 iOS 開發(fā)新手,一定是要緊跟前人腳步,學習使用 CocoaPods 。
基礎用法
這里簡單略過,請參考其他無數(shù)的文章。
安裝
系統(tǒng)默認安裝,可以參考其他教程 。在命令行下執(zhí)行。
- sudo gem install cocoapods
我的環(huán)境是 HomeBrew
- # 添加 taobao Mirror 不然被墻掉沒辦法下載
- gem sources -a http://ruby.taobao.org/
- # 安裝
- gem install cocoapods
- # 更新命令
- rbenv rehash
- # 執(zhí)行
- pod
- # 此時一般會下載官方的所有 PodSpec 庫,也可以用 pod setup 初始化環(huán)境
本文不打算在安裝部分耗費太多時間。希望看到這里保證你的命令行下有可用的 pod 命令。
使用
假設我們已經(jīng)有個項目,叫 ProjName ,需要使用一些注明的 CocoaPods 庫,比如 AFNetworking3.
首先,命令行 cd 到我們的項目目錄,一般 ls 命令會看到如下幾個文件夾
- ProjName
- ProjName.xcodeproj
- ProjNameTests
贊,就是這里,創(chuàng)建一個 Podfile 文本文件,寫入如下內(nèi)容
- platform :ios, "8.0"
- pod "AFNetworking", "~> 2.0"
一般這么簡單的文件都是直接 nano 寫。 :)
直接創(chuàng)建 Podfile , CocoaPods 會創(chuàng)建一個項目同名的 WorkSpace ,然后添加一個叫 Pods 的項目,這個項目編譯結(jié)果是一個叫 libPods.a的鏈接庫, 它會添加到我們之前的 ProjName 項目中作為編譯依賴。
當然,通過命令行執(zhí)行 pod init 也可以自動創(chuàng)建 Podfile,而且可以自動分析當前項目的 target ,相對來說更好,也更優(yōu)雅。具體請參考官方手冊。這樣的好處是更細致,還可以區(qū)分多個子項目子 target 。原理大同小異。
然后接下來,命令行執(zhí)行 open ProjName.xcworkspace,注意這個可不是 .xcodeproj,這個是 CocoaPods 為我們創(chuàng)建的一個 WorkSpace ,包含我們之前的項目,和 Pods 依賴。
開始編碼過程。直接在代碼里調(diào)用,比如寫在某個按鈕的 @IBAction 里:
- let manager = AFHTTPRequestOperationManager()
- let url = "http://api.openweathermap.org/data/2.5/weather"
- println(url)
- let params = ["lat": 39.26, "lon": 41.03, "cnt":0]
- println(params)
- manager.GET(url,
- parameters: params,
- success: { (operation: AFHTTPRequestOperation!,
- responseObject: AnyObject!) in
- println("JSON: " + responseObject.description!)
- },
- failure: { (operation: AFHTTPRequestOperation!,
- error: NSError!) in
- println("Error: " + error.localizedDescription)
- })
這里直接抄了 JakeLin 的 SwiftWeather 代碼4,就一小段,希望他不會打我。
Swift 坑爹了
看起來貌似我們已經(jīng)可以在 Swift 中使用 AFNetworking 了。結(jié)果剛寫幾句代碼一堆類和變量找不到定義,而且坑爹的是很多時候我們只能靠猜測,判斷這些 Objective-C 的定義轉(zhuǎn)換成 Swift 定義是什么樣子,用起來就是完全靠蒙!
這不科學!
這都三禮拜了,所以大家都摸索出了調(diào)用的方法,那就是按照和 Objective-C 代碼混編的例子,添加 Bridging Header !
繼續(xù)
之前簡單介紹過和 Objective-C 交互的內(nèi)容5,大家可以去圍觀。
一般說來,你在 Swift 項目新建 Objective-C 類的時候,直接彈出是否創(chuàng)建 Bridge Header 的窗口,點 YES 就是了,這時候一般多出來個 ProjectName-Bridging-Header.h 。然后刪掉這個類, Bridging Header 頭文件還在。
在這個 Bridging Header 文件里寫入要導入的 CocoaPods 庫,就可以在 Swift 中使用了。
- #import <AFNetworking/AFNetworking.h>
如果沒有自動創(chuàng)建頭文件的話,這個配置在項目的 Build Settings 中的 Swift Compiler – Code Generation 子項里。
創(chuàng)建一個頭文件,指定為 Bridging Header 也可以。
然后編譯,成功執(zhí)行!
這就完事了?
實際上,前兩天剛寫一篇 Swift 的模塊系統(tǒng) , 把任意 Objective-C 庫當做 Swift Module 是可行的。當時就覺得這個東西應該是可能完全進入 CocoaPods 的,但是在官方 repo 找了下發(fā)現(xiàn),以前有人提過增加 module.map 支持,結(jié)果 CocoaPods 的人認為這個是 llvm 內(nèi)部特性, issue 被關(guān)閉了。#2216 最近又被提起,我在后面提了下 Swift 支持,希望官方靠譜。
所以下面的內(nèi)容,就是,我們是否可以在 CocoaPods 上加入 module.map 支持,然后直接在 Swift 中 import ModuleName ?
擴展 CocoaPods
考慮了多種方式,***選擇了 Hook 的方式。如果 Ruby 技術(shù)足夠好,或許可以直接寫個插件?;蛘咧苯痈墓俜酱a給官方提交。但是實在能力有限。相關(guān)的 module.map 語法參考 llvm 官方手冊 Modules – Clang 3.5 documentation。用了最簡單的功能。也許遇到復雜的 PodSpec 就不起作用了,但是原理如此,相信小伙伴們已經(jīng)知道怎么做了。
目前我的 Podfile 大概是這個樣子:
- platform :ios, "8.0"
- pod "AFNetworking", "~> 2.0"
- pod "Baidu-Maps-iOS-SDK", "~> 2.0"
- post_install do |installer|
- File.open("#{installer.sandbox_root}/Headers/module.map", 'w') do |fp|
- installer.pods.each do |pod|
- normalized_pod_name = pod.name.gsub('-', '')
- fp.write <<EOF
- module #{normalized_pod_name} [system] {
- umbrella "#{pod.name}"
- export *
- }
- EOF
- puts "Generating Swift Module #{normalized_pod_name.green} for #{pod} OK!"
- end
- end
- end
post_install
是 Podfile
的一種 hook 機制,可以用來加入自定義操作。我在這里的寫的邏輯就是,針對所有的 Pod 生成一個 module.map
文件。 位于 Pods/Headers/
,這個目錄被 CocoaPods 自動設置為項目的 Header Search Path 所以不需要額外處理。默認我們的 Swift 文件就找得到。
其中 normalized_pod_name
用于處理百度地圖 API SDK 這一類名字帶減號的庫,因為他們不能作為 Module Name ,實際上或許有更好的方法來處理。
實際效果
實測發(fā)現(xiàn)完全沒有問題,直接 import AFNetworking
或者 import BaiduMapsiOSSDK
都可以。
而且很不錯的一點是,按住 Command 鍵,然后鼠標點擊模塊名、類名等,會跳轉(zhuǎn)到 Swift 定義。
坑
遇到提示 .pcm
文件 outdate 的情況下需要你刪除 $HOME/Library/Developer/Xcode/DerivedData/ModuleCache
目錄,這個目錄保存的是預編譯模塊,類似于預編譯頭文件。
目前 Swift 還是有很多 BUG 的,調(diào)用 NSObject
也許會讓編譯器直接 segment fault ,不帶任何出錯信息。很傷情。此時請***時間檢查語法是否有詭異,其次將所有用到字符串或者 Optional
的地方都額外用變量處理,避免用字面常量。(個人經(jīng)驗)
如果多次調(diào)用 pod install
并在其中修改過 Podfile
,那么有可能你的項目依賴會亂掉,多了不存在的 .a
文件到依賴或者多次包含。手工在項目樹和項目選項里刪除就可以了。此類編譯錯誤都是鏈接錯誤。
總結(jié)
本文提出了一種 Bridging Header 之外的使用 CocoaPods 庫的方法。利用有限的 Ruby 知識寫了個 Hook 。目前測試 OK 。
參考
- CocoaPods Offical Site CocoaPods 官網(wǎng)↩
- CocoaPods – CocoaChina CocoaChina 對 CocoaPods 的介紹↩
- AFNetworking – Github↩
- SwiftWeather↩
- Swift and ObjectiveC Interop (Swift 與 Objective-C 之間的交互)↩