在 Swift 中使用 CocoaPods

發表於2014-06-26

本文介紹如何在 Swift 專案中使用 CocoaPods 。如果你已經精通 Bridging Header 的方法,請直接跳到 “擴充套件 CocoaPods” 一節。

什麼是 CocoaPods

CocoaPods is the dependency manager for Objective-C projects. It has thousands of libraries and can help you scale your projects elegantly. 1

從介紹看,它是主要給 Objective-C 專案用的,但是我們可以很容易地混合 Objective-C 和 Swift 到同個專案,從而利用大量的 CocoaPods 庫和 Swift 漂亮舒服的語法。

作為 iOS 開發新手,一定是要緊跟前人腳步,學習使用 CocoaPods 。

基礎用法

這裡簡單略過,請參考其他無數的文章。

安裝

系統預設安裝,可以參考其他教程2 。在命令列下執行。

我的環境是 HomeBrew

本文不打算在安裝部分耗費太多時間。希望看到這裡保證你的命令列下有可用的 pod 命令。

使用

假設我們已經有個專案,叫 ProjName ,需要使用一些註明的 CocoaPods 庫,比如 AFNetworking3.

首先,命令列 cd 到我們的專案目錄,一般 ls 命令會看到如下幾個資料夾

贊,就是這裡,建立一個 Podfile 文字檔案,寫入如下內容

一般這麼簡單的檔案都是直接 nano 寫。 :)

直接建立 Podfile , CocoaPods 會建立一個專案同名的 WorkSpace ,然後新增一個叫 Pods 的專案,這個專案編譯結果是一個叫 libPods.a的連結庫, 它會新增到我們之前的 ProjName 專案中作為編譯依賴。

當然,通過命令列執行 pod init 也可以自動建立 Podfile,而且可以自動分析當前專案的 target ,相對來說更好,也更優雅。具體請參考官方手冊。這樣的好處是更細緻,還可以區分多個子專案子 target 。原理大同小異。

然後接下來,命令列執行 open ProjName.xcworkspace,注意這個可不是 .xcodeproj,這個是 CocoaPods 為我們建立的一個 WorkSpace ,包含我們之前的專案,和 Pods 依賴。

開始編碼過程。直接在程式碼裡呼叫,比如寫在某個按鈕的 @IBAction 裡:

這裡直接抄了 JakeLin 的 SwiftWeather 程式碼4,就一小段,希望他不會打我。

Swift 坑爹了

看起來貌似我們已經可以在 Swift 中使用 AFNetworking 了。結果剛寫幾句程式碼一堆類和變數找不到定義,而且坑爹的是很多時候我們只能靠猜測,判斷這些 Objective-C 的定義轉換成 Swift 定義是什麼樣子,用起來就是完全靠蒙!

這不科學!

這都三禮拜了,所以大家都摸索出了呼叫的方法,那就是按照和 Objective-C 程式碼混編的例子,新增 Bridging Header !

繼續

之前簡單介紹過和 Objective-C 互動的內容5,大家可以去圍觀。

一般說來,你在 Swift 專案新建 Objective-C 類的時候,直接彈出是否建立 Bridge Header 的視窗,點 YES 就是了,這時候一般多出來個 ProjectName-Bridging-Header.h 。然後刪掉這個類, Bridging Header 標頭檔案還在。

在這個 Bridging Header 檔案裡寫入要匯入的 CocoaPods 庫,就可以在 Swift 中使用了。

如果沒有自動建立標頭檔案的話,這個配置在專案的 Build Settings 中的 Swift Compiler – Code Generation 子項裡。

建立一個標頭檔案,指定為 Bridging Header 也可以。

然後編譯,成功執行!

這就完事了?

實際上,前兩天剛寫一篇 Swift 的模組系統 , 把任意 Objective-C 庫當做 Swift Module 是可行的。當時就覺得這個東西應該是可能完全進入 CocoaPods 的,但是在官方 repo 找了下發現,以前有人提過增加 module.map 支援,結果 CocoaPods 的人認為這個是 llvm 內部特性, issue 被關閉了。#2216 最近又被提起,我在後面提了下 Swift 支援,希望官方靠譜。

所以下面的內容,就是,我們是否可以在 CocoaPods 上加入 module.map 支援,然後直接在 Swift 中 import ModuleName ?

擴充套件 CocoaPods

考慮了多種方式,最後選擇了 Hook 的方式。如果 Ruby 技術足夠好,或許可以直接寫個外掛。或者直接改官方程式碼給官方提交。但是實在能力有限。相關的 module.map 語法參考 llvm 官方手冊 Modules – Clang 3.5 documentation。用了最簡單的功能。也許遇到複雜的 PodSpec 就不起作用了,但是原理如此,相信小夥伴們已經知道怎麼做了。

目前我的 Podfile 大概是這個樣子:

post_install 是 Podfile 的一種 hook 機制,可以用來加入自定義操作。我在這裡的寫的邏輯就是,針對所有的 Pod 生成一個 module.map 檔案。 位於 Pods/Headers/,這個目錄被 CocoaPods 自動設定為專案的 Header Search Path 所以不需要額外處理。預設我們的 Swift 檔案就找得到。

其中 normalized_pod_name 用於處理百度地圖 API SDK 這一類名字帶減號的庫,因為他們不能作為 Module Name ,實際上或許有更好的方法來處理。

實際效果

實測發現完全沒有問題,直接 import AFNetworking 或者 import BaiduMapsiOSSDK 都可以。

而且很不錯的一點是,按住 Command 鍵,然後滑鼠點選模組名、類名等,會跳轉到 Swift 定義。

遇到提示 .pcm 檔案 outdate 的情況下需要你刪除 $HOME/Library/Developer/Xcode/DerivedData/ModuleCache 目錄,這個目錄儲存的是預編譯模組,類似於預編譯標頭檔案。

目前 Swift 還是有很多 BUG 的,呼叫 NSObject 也許會讓編譯器直接 segment fault ,不帶任何出錯資訊。很傷情。此時請第一時間檢查語法是否有詭異,其次將所有用到字串或者 Optional 的地方都額外用變數處理,避免用字面常量。(個人經驗)

如果多次呼叫 pod install 並在其中修改過 Podfile,那麼有可能你的專案依賴會亂掉,多了不存在的 .a 檔案到依賴或者多次包含。手工在專案樹和專案選項裡刪除就可以了。此類編譯錯誤都是連結錯誤。

總結

本文提出了一種 Bridging Header 之外的使用 CocoaPods 庫的方法。利用有限的 Ruby 知識寫了個 Hook 。目前測試 OK 。

參考

 

 

 

 

相關文章