Use CocoaPods With Swift (在 Swift 中使用 CocoaPods)

http://andelf.github.io/blog/2014/06/23/use-cocoapods-with-swift/



聲明: 轉載註明我或者 SwiftChina 。請在方便的情況下情儘量告知. weibo

本文的發現基於個人研究。請尊重原創。已授權 CocoaChina 轉載個人文章。

本文介紹如何在 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 。在命令行下執行。

sudo gem install cocoapods

我的環境是 HomeBrew

1
2
3
4
5
6
7
8
9
# 添加 taobao Mirror 不然被牆掉沒辦法下載
gem sources -a http://ruby.taobao.org/ 
# 安裝
gem install cocoapods
# 更新命令
rbenv rehash
# 執行
pod
# 此時一般會下載官方的所有 PodSpec 庫,也可以用 pod setup 初始化環境

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

使用

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

首先,命令行 cd 到我們的項目目錄,一般 ls 命令會看到如下幾個文件夾:

1
2
3
ProjName
ProjName.xcodeproj
ProjNameTests

贊,就是這裏,創建一個 Podfile 文本文件,寫入如下內容

1
2
platform :ios, "8.0"
pod "AFNetworking", "~> 2.0"

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

直接創建 Podfile , CocoaPods 會創建一個項目同名的 WorkSpace ,然後添加一個叫 Pods 的項目,這個項目編譯結果是一個叫 libPods.a的鏈接庫, 它會添加到我們之前的 ProjName 項目中作爲編譯依賴。

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

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

開始編碼過程。直接在代碼裏調用,比如寫在某個按鈕的 @IBAction 裏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    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 坑爹了

看起來貌似我們已經可以在 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 中使用了。

#import <AFNetworking/AFNetworking.h>

如果沒有自動創建頭文件的話,這個配置在項目的 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 大概是這個樣子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 ,實際上或許有更好的方法來處理。

實際效果

實測發現完全沒有問題,直接 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 。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章