利用 CocoaPods 及 GitHub  一步步創建可重用的函式庫!


歡迎回到我們的教學系列文章!在上一篇教學文章中,你學到了如何使用 Network 框架來偵測及監控網路狀態。看來這個框架在 NetStatus 類別中運行得相當不錯,所以讓我們更進一步,來創建一個基於 NetStatus 類別的小型、開源框架吧!在本篇教學中,我們將透過 CocoaPods 來創建一個 pod,讓這個框架容易發佈及整合,並把它推送到 GitHub 上。

要在多個專案中重複使用 NetStatus 是非常簡單的。而且,將來你希望創建的任何自定義框架,都可以依照接下來所教的方式來實現。

備註: 如果你還沒有看過之前的教學文章,我強烈建議你可以先去閱讀一下。如果你只是想要學習如何創建一個 pod, 請先下載此專案

創建框架

好,讓我們先來創建一個可以發佈的函式庫。

首先,在 Xcode 打開 File > New > Project… 來創建一個新專案。在第一個步驟裡,選擇 Cocoa Touch Framework 為樣板。

Cocoapods tutorial - new project

接著,設定 Team、Organization Name、和 Organization Identifier 等相關專案屬性,並將產品名稱設定為我們的類別名稱:NetStatus

cocoapods-product_name

備註:你可以自由為框架的產品名稱命名,我將會使用 NetStatus,所以從現在開始 NetStatus 就代表我們所創建出的客製化框架,除非有特別說明,否則這不會是代表類別名稱。

最後,選擇一個資料夾來儲存我們的新專案。我建議在 NetStatusDemo 專案所在的同一個根目錄底下,創建一個名為 NetStatus 的子資料夾,並儲存在那裡。

創建好專案後,你會發現當中的檔案不多。大部份的情況下,你並不需要進行任何更動,除了變更 Version Number 與 Deployment Target 之外。

cocoapods-target_settings

說到這一點,請將 Deployment Target 更改為 12.0,那我們就可以在運行 iOS 12.0 以上版本的設備上使用此框架。

一般來說,最好的方法就是創建一個群組 (group) 來添加資源檔案,並將它們與專案的其餘內容分開。因此,右鍵單擊 NetStatus 根群組,並創建一個名為 Source 的新群組。

現在切換到 NetStatusDemo 專案,右鍵單擊 NetStatus.swift 檔案,從右邊選單中選擇 Show in Finder 的選項。

NetStatus.swift 檔案在 Finder 中顯示後,將 NetStatus Xcode 專案的視窗維持在前面,並將 NetStatus.swift 拖放到 Xcode 的 Source 組別之中。拖動後,請確認你有勾選 Copy items if needed 的選項,並選擇 NetStatus 為目標。

cocoapods-add_source

框架能夠運行嗎?

上面的所有步驟,都是基於 NetStatus 類別來創建屬於自己的小型框架需要的步驟。但它真的有效嗎?讓我們驗證一下!首先,關閉我們剛剛所創建的 NetStatus 專案。然後,在 NetStatusDemo 專案之中,從 Project Navigator 選擇並刪除 NetStatus.swift 檔案。當你被詢問時,請將其移至垃圾桶。

delete_original_source

現在,在 Finder 找到 NetStatus Xcode 專案(NetStatus.xcodeproj 檔案),將它拖放到 NetStatusDemo 之中。

備註:再次提醒,在將它拖放到 NetStatusDemo 之前,請確認你已經關閉了 NetStatus 專案

cocoapods-add_framework

請記住,如果要刪除剛剛從 NetStatusDemo 專案拖動的專案,則應該僅刪除引用,而非移動到垃圾桶,否則你將會刪除 NetStatus 的框架檔案。

打開 NetStatusDemo target,並切換到 General 分頁。點擊 Embedded Binaries 中的 plus (+) 按鈕,然後從出現的對話框中選擇 NetStatus.framework,所選框架也會顯示在 Linked Frameworks 和 Binaries 的欄位之中。

cocoapods-link_framework

要一次建構你的專案,你可以在鍵盤上按 Cmd + B,或是透過 Xcode 裡面的 Product > Build 選項。專案建構完成後,打開 ViewController.swift 檔案,並且匯入 NetStatus 框架。

import_framework

再次建構專案,你將會看到錯誤訊息多次出現!不過錯誤為甚麼會發生?

error_message_1

這一切都跟模組在 Swift 的存取控制與預設的存取級別有關係,NetStatus 類別預設是 internal 的存取級別,這代表它只能被它內部模組 NetStatus 框架所存取,以目前的設定 NetStatusDemo 是沒有辦法看到它的,所以我們只要針對這點做出修正即可。

打開 NetStatus.xcodeproj 檔案,並展開 NetStatus 底下的 Source 群組,你會在裡面找到 NetStatus.swift 檔案。順帶一提,我推薦實作客製化框架的方式,是將其嵌入到範例專案之中,並隨時發現此類問題。另外,對嵌入式專案所做的更改並不會立即顯示給其他模組,因此每次要對範例專案進行任何更改的時候,都要進行建構。

現在打開 NetStatus.swift 檔案,並將類別定義更新為如下所示:

再次建構專案,並打開 ViewController.swift 檔案,我們可以發現錯誤依然存在,不過這次的錯誤訊息與之前不同:

error_message_2

這一次的錯誤訊息意思更加明確,我們再一次的面對了存取級別的限制。shared 實例不能夠被存取,因為它預設是 internal 的存取級別。若要更清楚地描述,你可以看到 NetStatus.swift 檔案中,我們將分享實例定義如下:

這等同於:

內部級別是被自動推論出來的,這並不需要明確的寫出來。現在,假如我們想要使分享實例能夠被 NetStatusDemo 所看見,我們所需要做的就是將上列的定義改為:

再重新建構一次專案,你會看到現在同樣的錯誤訊息已經不再出現,不過還是出現了同一類的錯誤。預設的存取級別阻止了 NetStatusDemo 存取我們在 NetStatus 類別中實現的屬性和方法。

multiple_errors

現在讓我們來一次修正所有問題,除了 monitor 之外,在所有屬性前面加上 public 關鍵字:

startMonitoring()stopMonitoring() 方法做同樣的動作:

備註 因為我們需要從 NetStatusDemo 模組中看到大部分的屬性,這使它們都公開了。一般來說,我們只需要公開那些在自己模組中所需要的屬性即可。

再次建構專案,這次沒有錯誤出現了!恭喜你 👏 !

創建 Pod

當我們在建構 iOS App 的時候,加入第三方函式庫是很常見的情況,而管理函式庫最好的方式,就是使用像是 CocoaPods套件管理員 (Dependency Manager)。我們將會利用它來創建自己的 pod,我們將會先透過本地的 pod 來安裝 NetStatus 框架,而遠端的方式透過 GitHub 來實現。假如你還沒安裝 CocoaPods,你可以跟著網站的指示立即安裝。

現在打開 Terminal,並切換到 NetStatus 框架所在的資料夾位置。如果 New Terminal at Folder 功能已經被開啟,這個動作可以很輕易地達成,你只需要右鍵點擊 NetStatus 資料夾,並打開 Services 子項目選單就可以看到:

terminal_at_folder

如果你在 mac 無法找到這個功能,你可能要先到這裡看看如何開啟它。

打開 Terminal,並輸入下列指令:

你應該會在 Finder 中看到一個訊息指出「Specification created at NetStatus.podspec」,和一個名為 NetStatus.podspec 的新檔案。

使用編輯器或 Xcode 打開這個檔案。如果你是第一次看到這種檔案,可以仔細瞧瞧它包含了哪些資訊。最後,利用以下的部分替換原有內容:

現在花點時間來看看我們在這裡做了甚麼設定,以上包含了我們必須提供給 pod 規範文件的最少資訊。

你可以自由地更改 pod 的摘要和說明,說明應該要比摘要還要來得長,否則你將會收到警告訊息。此外,首頁應該要連結到與這個 pod 所關注框架相關的網站,但是目前我們還沒有這樣的網頁,所以我們將會使用 AppCoda 的網址來代替。另外,你也可以提供你的名字和電子郵件來更新作者資訊。

關於其餘部分,spec.platform 指定要安裝的框架所需的最低 iOS 版本。 spec.swift_version 定義了用於驗證框架程式碼的 Swift 版本。spec.source_files「說」源文件存在於框架的文件夾結構中。

而剩下的部分,spec.platform 指出了要安裝的框架所需要的最低 iOS 版本,spec.swift_version 定義了用來驗證框架程式碼的 Swift 版本。spec.source_files 代表說明文件存在於框架的文件夾結構當中。

請注意這一行:

這是一種解決方法,用來將當前的資料夾指定為框架的來源。在早期的 CocoaPods 版本當中,有一個名為 :pathkey,但它已經停止支援了。我們暫時保留它作為來源,因為在 GitHub 上 NetStatus 框架還沒有對應的儲存庫。

現在在終端機上面執行以下命令,來確認 pod 是否有效:

你也可以指定檔案名稱:

在驗證 pod 規範的時候,你會需要等候一下。假如框架的資料夾路徑不能明確地被辨認,驗證有可能會失敗,不過現在已經可以被接受了。我們馬上就會改變來源。

現在回到 Xcode,選擇 NetStatus.xcodeproj 目標,右鍵點擊並刪除它,請確保你有選到 Remove Reference 而不是 Move to Trash,否則你將會刪除原始的來源檔案。最後,關閉專案

現在,開啟一個新終端機視窗(或是在現有的終端機中開啟一個新頁籤),並切換到範例專案 NetStatusDemo 所在的資料夾位置,輸入下列指令來創建一個 Podfile

在編輯器中打開我們創建的 Podfile,並用下列程式碼來更新它:

這裡我們指出了 pod 規範文件所在的路徑。照理來說,NetStatus 專案的根目錄就是 NetStatusDemo 專案資料夾的所在位置。

same_root_folder_projects

儲存它,並回到終端機輸入:

透過我們所先前創建的 pod 規範文件,你應該能夠看到 NetStatus 框架成功安裝了,你可以查看範例專案資料夾裡面的內容來確認。Pods 子資料夾應該會在你輸入剛剛那行 pod 安裝指令之後出現:

pods_subfolder

當你使用 CocoaPods 來安裝框架時,一個 Xcode workspace 會在安裝 pod 的過程中自動被產生出來,從現在開始你應該使用它來開啟專案。所以請雙擊來打開 NetStatusDemo.xcworkspace,而不是 NetStatusDemo.xcodeproj

在 Xcode 打開了工 workspace 後,按下 Cmd + B 來再次建構範例專案。如果你有好好地跟著我們的步驟,就應該能夠成功建構專案!

請注意,按照我們在這裡執行的方式安裝本地 pod 時,你仍然可以編輯框架的來源檔案。展開 Pods 目標,然後展開 Development Pods> NetStatus 群組,你就會看到 NetStatus.swift 文件。請記住要建構專案,以便範例專案可以看到所有變動。

將框架加入到 GitHub 資源庫中

通常 pod 不會創建於本地端留存和使用,大多數時候,開發人員會在 GitHub 上創建一個儲存庫,並將框架與 pod 放在那裡。如果儲存庫是公開的,那麼任何人都可以使用 CocoaPods 安裝框架;如果儲存庫沒有公開,只有知道私有 GitHub 儲存庫的人才能通過 pod 安裝框架。

若要按照本段落中所描述的步驟操作,你需要先擁有 GitHub 的帳號,或是 Bitbucket 帳號,但我不會在這裡使用 Bitbucket 來詳細介紹。在開始之前,請連接你的 GitHub 帳號,然後點擊右上角的加號 (+) 按鈕創建新的儲存庫。

第一步是為儲存庫提供有效的名稱,NetStatus 是個不錯的選擇。接著,可選擇提供簡短描述。然後,將儲存庫標記為 Private,啟用 initialize with a Readme file 選項,為 Swift 添加 .gitignore 文件,並選擇 MIT許可證。閱讀每種許可類型的用途,如果需要的話,你可以隨意選擇另一種許可類型。

new_repository

最後,點擊Create repository 來建立新儲存庫。

進入儲存庫的主頁面後,點擊綠色的 Clone or download 按鈕,並複製儲存庫的 URL。

copy_repo_url

現在,到框架和範例專案的根目錄,打開一個新終端機視窗或新頁籤。鍵入以下的內容以創建新的子目錄,然後輸入:

現在,從 GitHub 上複製儲存庫,輸入:

貼上剛剛從 GitHub 頁面複製的 URL,上面指令對我來說會像是這樣:

儲存庫將會被複製到 NetStatus 子資料夾的 repo 目錄底下。

repo_dir

在終端機之中導航到該目錄:

現在,在 Finder 切換到框架所在的目錄,並將所有的內容複製到剛創建的新目錄中。選取 NetStatus.xcodeproj,NetStatus 子目錄、和 Netstatus.podspec,並將它們複製貼上到 /repo/NetStatus 目錄中:

copy_framework_to_cloned_dir

或者用另一種方式,假如你和我使用一樣的目錄結構,你可以透過終端機一次完成所有步驟:

在這個時候,你可以自由編輯 Readme 檔案,並加入任何內容。

現在,讓我們來更新 /repo/NetStatus 目錄下的 .podspec 檔案。刪除指出本地路徑作為來源的那一行,並將來源指向 GitHub 儲存庫那一行的註解解除。

請確認上面所提供的 URL 是你自己的儲存庫,舉例來說,我的會像是這樣:

儲存 NetStatus.podspec 檔案,並回到終端機。是時候更新儲存庫了,而第一步就是將所有新和編輯過的檔案加到 git:

接下來,讓我們用簡短的敘述來提交變更的部分。

最後,將所有東西再次推送到 GitHub 上面:

當整個過程結束之後,重新整理 GitHub 的儲存庫,你就會看見上面所有的檔案已經被推送上去。

cocoapods-pushed_files

除了上述內容之外,我們應該標籤 (tag) 儲存庫來讓它與 .podspec 檔案中的 spec.version 數字相符。

記得每次都要依照 spec.version 的數值來更新標籤,這樣一來 pod 才能夠對應到適當的儲存庫版本。

現在輸入下列程式碼,來再次驗證 .podspec 檔案:

這一次驗證應該會成功地顯示「NetStatus.podspec passed validation.

現在讓我們試著從 GitHub 儲存庫來安裝框架。到 NetStatusDemo 資料夾,並如下編輯 Podfile:

利用你儲存庫的 URL 來更換掉假的 URL,並儲存檔案。接著在 NetStatusDemo 專案的所在位置打開新的終端機,或是在之前已經打開了的終端機中輸入:

備註請確認在執行上述指令前,NetStatusDemo.xcworkspace 沒有在 Xcode 中打開。

cocoapods-pod_install

如果你看到了上述的警告訊息,只需要按照終端機中的指示來對 NetStatusDemo 專案操作就能解決。

現在,再次在 Xcode 中打開 NetStatusDemo.xcworkspace,並建構專案來確認一切都沒問題。NetStatus 框架成功地由遠端的來源被安裝了!

將 Pod 推送到公共倉庫

現在是我們所需要做的最後一步,就是如何公開你的框架讓所有人都可以使用,就如你在自己的專案中使用別人的框架一樣。首先,我們需要將你的儲存庫在 GitHub 上面公開,這很簡單,你只需要在儲存庫設定裡的 Danger ZoneSettings > Make Public 做更改。

你可以在這裡看看下一步的原始指南。首先,你需要使用電子郵件來註冊一個帳號,這個帳號會連接到你要註冊的設備:

請留意你的電子信箱,你將會收到一封驗證電郵,點擊當中的連結來完成驗證。下一步,就是將 pod 推送到公共倉庫上:

把「PODNAME」更改為適當的名稱並稍等一下,如果沒有問題的話,你將會在終端機收到成功的訊息,否則你將會收到程序失敗的原因。如果你是看到警告訊息,又想要忽略的話,請在上面的指令後面加入 --allow-warnings,像這樣:

請注意,你不用每次推送 pod 都註冊一個新帳號。

總結

今天我們簡單講解了幾個不同的步驟,除了 Xcode 之外,我們還把重心放到了其他部分。文章闡述了你在 GitHub 上發佈開源框架、並讓其他人也可以使用的步驟。我希望你喜歡今天的教學內容,並學到了一些新東西。下次再會 👋!

譯者簡介:HengJay,iOS 初學者,閒暇之餘習慣透過線上 MOOC 資源學習新的技術,喜歡 Swift 平易近人的語法也喜歡狗狗,目前參與生醫領域相關應用的 App 開發,希望分享文章的同時也能持續精進自己的基礎。

LinkedIn: https://www.linkedin.com/in/hengjiewang/
Facebook: https://www.facebook.com/hengjie.wang

原文:Creating Reusable Libraries with CocoaPods and GitHub


資深軟體開發員,從事相關工作超過二十年,專門在不同的平台和各種程式語言去解決軟體開發問題。自2010年中,Gabriel專注在iOS程式的開發,利用教程與世界上每個角落的人分享知識。可以在Google+或推特關注 Gabriel。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This