一、準備 組件化 + 隨著業務需求的增長,在單工程 MVC 模式下,app 代碼逐漸變得龐大,面對的高耦合的代碼和複雜的功能模塊,我們或許就需要進行重構了,以組件化的形式,將需要的組件以 pod 私有庫的形式安裝到最後的主工程中,組件間各自獨立、解耦,僅依賴中間件進行通信,這或許就是極好的架構形式。 ...
一、準備
- 組件化
- 隨著業務需求的增長,在單工程 MVC 模式下,app 代碼逐漸變得龐大,面對的高耦合的代碼和複雜的功能模塊,我們或許就需要進行重構了,以組件化的形式,將需要的組件以 pod 私有庫的形式安裝到最後的主工程中,組件間各自獨立、解耦,僅依賴中間件進行通信,這或許就是極好的架構形式。
- 使用 CocoaPods
- 如果你的 Mac 系統升級過了,避免出現莫名的問題,強烈建議重裝 CocoaPods 及更新 ruby。(可參考 http://www.jianshu.com/p/8169f5d7f364)
- CocoaPods 下載框架的原理及 spec、Podfile 文件,可參考 http://www.jianshu.com/p/8a7b9232cbab 或 http://blog.csdn.net/morenyaojing/article/details/53376475 。
- CocoaPods 預設的 spec repo,是基於 git 的,可創建基於 git 管理的私有庫 spec repo,因公司性質及要求,代碼一律使用內部 svn 管理。因此本文完全使用 svn,來進行私有庫製作。(使用 git 的文章網上很多,更方便,其實最終感覺也都差不多)
- 安裝 cocoapods-repo-svn 插件,參考 https://github.com/dustywusty/cocoapods-repo-svn 。
- 代碼文件來源
- 因公司項目原因,本文以網上的 demo 代碼來演示。demo 來源 https://github.com/sun6boys/CRMainProject
二、製作基於 svn 的私有 sepc repo
- 使用 svn 管理,先在 svn 伺服器上新建一個放 spec 的遠程倉庫,本文以 SpecRepo 命名,該倉庫不需要創建標準的 trunk、tags、branches 目錄,僅僅是放組件的 spec 文件的。
- 使用該 SpecRepo svn 地址建立私有 repo,命令行操作如下
pod repo-svn add SpecRepo http://10.211.55.3/svn/SpecRepo
- 這裡是 svn 管理的,利用的是 cocoapods-repo-svn 插件,如果是 git 管理的,那就是預設的
pod repo add xxx git地址
建立了私有 SpecRepo,其本地
.cocoapods/repos
文件夾目錄如下
使用
pod repo
命令查看 repo 如下
- 這裡是 svn 管理的,利用的是 cocoapods-repo-svn 插件,如果是 git 管理的,那就是預設的
三、基本的組件化的創建
- 建立 CRProtocolManager 的組件私有庫
- 使用 svn 管理,先在 svn 伺服器上新建一個 CRProtocolManager 的倉庫,因為是代碼文件且需要使用 tag,所以使用標準的 trunk、tags、branches 目錄
- 使用
pod lib create CRProtocolManager
命令來下載帶有預設模板的庫,之後基於預設模板進行修改- 使用該命令後最回答幾個問題,分別是姓名、郵箱(用於 spec 文件中的作者信息)、選擇語言(有 Swift 和 ObjC,這裡選擇 ObjC)、是否需要 demo 工程(這個是需要的,利用這個 demo 進行組件測試)、選擇測試庫(這裡選 None)、do view based testing(這裡選 No)
- 在 Finder 中,將 CRProtocolManager 的核心文件複製到 demo 工程中的 CRProtocolManager 下 Classes 文件夾下,並刪除模板文件 ReplaceMe.m
- 其中 Assets 文件夾下放與該組件有關的圖片等資源文件
- 其中 Assets 文件夾下放與該組件有關的圖片等資源文件
- 命令行中 cd Example 工程目錄下,
pod install
來更新工程的 CRProtocolManager 核心代碼- 該工程目錄下 Podfile 文件中
pod 'CRProtocolManager', :path => '../'
,指向的外層文件夾下的 CRProtocolManager.podspec 文件,因此可以這樣添加刪除文件後使用pod install
來更新核心代碼到工程里
- 該工程目錄下 Podfile 文件中
- 添加完核心代碼後,更改 CRProtocolManager.podspec 文件
- 一個是 s.version 這個和 svn 要打 tag 的編號是一致,例如
s.version = '1.0'
- 一個是
s.source = {:svn =>'http://10.211.55.3/svn/CRProtocolManager', :tag => s.version.to_s }
,原來 s.source 是 git 地址,這裡改為 svn 地址,指向 svn 代碼倉庫。 - s.source_files、s.license、s.name(名字和svn倉庫名、創建的組件名最好統一)可保持不變
- s.summary、s.description、s.homepage、s.author 這些描述性的信息按需修改
s.ios.deployment_target = '8.0' 按需修改
# 示例 Pod::Spec.new do |s| s.name = 'CRProtocolManager' s.version = '1.0' s.summary = 'CRProtocolManager.' s.description = <<-DESC CRProtocolManager xxxx DESC s.homepage = 'http://www.baidu.com' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { 'HOWIE-CH' => '[email protected]' } s.source = {:svn =>'http://10.211.55.3/svn/CRProtocolManager', :tag => s.version.to_s } s.ios.deployment_target = '8.0' s.source_files = 'CRProtocolManager/Classes/**/*' # s.resource_bundles = { # 'CRProtocolManager' => ['CRProtocolManager/Assets/*.png'] # } # s.frameworks = 'UIKit', 'MapKit' # s.dependency 'AFNetworking', '~> 2.3' end
- 一個是 s.version 這個和 svn 要打 tag 的編號是一致,例如
- 將更改好的、測試好的組件提交到 svn CRProtocolManager 倉庫 trunk 下,註意:因為模板是從 github.com 上 clone 的,所以這裡先把 .git 文件夾刪掉。(本文使用的是 smartSVN 管理工具)
- 對代碼進行打 tag,且編號為 1.0,與 spec 文件里的 s.version 一致。
- 將 CRProtocolManager.podspec 文件提交到之前製作好的私有 sepc repo 中
- cd 到 CRProtocolManager.podspec 所在目錄,使用
pod repo-svn push SpecRepo CRProtocolManager.podspec
- 此時 svn 倉庫和 .cocoapods/repo/SpecRepo 下都有 CRProtocolManager.podspec
- 此時 svn 倉庫和 .cocoapods/repo/SpecRepo 下都有 CRProtocolManager.podspec
- 可在此之前進行 spec 文件有效性檢查,
pod repo-svn lint CRProtocolManager.podspec
- cd 到 CRProtocolManager.podspec 所在目錄,使用
- 使用 CRProtocolManager 的私有庫組件
- xcode 新建一個工程,使用
pod init
創建 Podfile 文件 Podfile 中預設是搜索 .cocoapods/repo/master 對應的官方預設公用庫的源,因此需要 Podfile 中添加本地 repo 源的地址
platform :ios, '8.0' target 'test' do plugin 'cocoapods-repo-svn', :sources => [ 'http://10.211.55.3/svn/SpecRepo' # 添加 svn 伺服器中私有庫 spec 的 repo ] use_frameworks! pod 'CRProtocolManager' # 本地 svn 私有庫 pod 'AFNetworking' # 可直接 pod 公開第三方庫 end
- git 的話要同時標明私有 repo 地址和預設 repo 地址(以 source 'git地址' 形式)
- 直接使用
pod install
即可安裝 CRProtocolManager 私有組件 - 會出現的問題
pod install
報錯找不到組件,可使用pod repo remove SpecRepo
後重新添加pod repo-svn add SpecRepo http://10.211.55.3/svn/SpecRepo
,然後pod repo-svn update SpecRepo
,若還沒解決可能是 ruby、CocoaPods 版本不是最新的原因,建議重裝後操作。- CRProtocolManager.podspec 文件提交到私有 sepc repo中後,
pod search CRProtocolManager
搜索不到,可到/Users/HOWIE-CH/Library/Caches/CocoaPods
下刪除 search_index.json 文件後重試。
- xcode 新建一個工程,使用
四、私有庫和私有庫之間的依賴、私有庫依賴共有庫及 subspec 的應用
其他組件和第三步一樣進行操作,最後 svn 伺服器截圖和 SpecRepo 文件夾截圖如下
- 依賴關係,例如對 CRGoodsDetail 進行探索 svn 的製作私有庫之間的依賴的解決
- CRGoodsDetail 中 CRGoodsDetailServiceProvide 依賴 CRGoodsDetailServiceProtocol 和 CRProtocolManager,CRGoodsDetailViewController 依賴 CRConfirmOrderServiceProtocol 和 CRProtocolManager。
- pod lib create 創建好工程,進行核心的代碼的拷貝和工程的更新操作之後,直接編譯會報錯的,因為存在依賴關係
在 CRGoodsDetail.podspec 文件中添加
# 需要的依賴私有庫 s.dependency 'CRProtocolManager' s.dependency 'CRConfirmOrderServiceProtocol' s.dependency 'CRGoodsDetailServiceProtocol' # 依賴公有庫 ,可添加依賴的公有庫 s.dependency 'SDWebImage'
在工程目錄下的 Podfile 文件下添加私有 repo 的 svn 地址,才能下載私有庫
use_frameworks! target 'CRGoodsDetail_Example' do # 添加私有 repo 的 svn 地址 plugin 'cocoapods-repo-svn', :sources => [ 'http://10.211.55.3/svn/SpecRepo' ] pod 'CRGoodsDetail', :path => '../' target 'CRGoodsDetail_Tests' do inherit! :search_paths end end
- pod install 就會裝好依賴庫,包括依賴的私有庫和公有庫
- 若出現問題則參考標題三中的解決方案
- 該 demo 工程 Podfile 文件的 pod 'CRGoodsDetail' 指向上一個目錄的 CRGoodsDetail.podspec 文件,因此會檢測 s.dependency 依賴的庫,依賴的私有庫是在從 Podfile 中添加的私有 repo 的 svn 地址中去找的
- 該 demo 工程運行測試後沒有報錯後,要提交 svn,打(新) tag(組件的迭代開發維護),提交 spec 文件到私有 repo 中
- subspec 的使用
- 例如 CRGoodsDetail 中可分為模塊文件(MVC 文件)及 Provider 文件,因此可使用 subspec
CRGoodsDetail 核心代碼中分文件夾
CRGoodsDetail.podspec 文件中
# subspec 及各自的依賴私有庫 s.subspec 'Module' do |m| m.source_files = 'CRGoodsDetail/Classes/Module/*' # 依賴私有庫 m.dependency 'CRConfirmOrderServiceProtocol' # 依賴公有庫 m.dependency 'SDWebImage' end s.subspec 'Provider' do |p| p.source_files = 'CRGoodsDetail/Classes/Provider/*' p.dependency 'CRGoodsDetailServiceProtocol' end # 公用的依賴私有庫 s.dependency 'CRProtocolManager'
使用 subspec 後 pod search 效果
Podfile 中使用 subspec
# pod 'CRGoodsDetail', :subspecs =>['Module'] #只載入本地 svn 私有庫的某個 subspec pod 'CRGoodsDetail' # 完整的 svn 私有庫
五、圖片資源、xib文件等
- 創建的 demo 工程中,組件相關的圖片是放到 Assets 文件下
- spec 文件中需要添加
s.resource_bundles = { 'CRGoodsDetail' => ['CRGoodsDetail/Assets/*'] }
- 組件代碼中載入圖片不在是直接 imageName 了,圖片打包的是一個 bundle 且 整個組件並不在 mainbundle 中,而是先要找到組件對應的 framework 包(可查看 Products 下,編譯的 app 里的結構),然後找圖片路徑進行載入
- 組件中的 xib
- 使用 xib,以前的工程,xib 在 mainbundle 中。以組件形式的話,xib 在組件對應的 framework 包中
- xib 中的圖片路徑,跟上面一樣需要添加圖片資源包的相對路徑
六、其他
- 使用 svn 的話,會提示輸入用戶名、密碼,有時會因此報錯,建議一開始就直接命令行
svn checkout xxx
一次,讓電腦記住用戶名、密碼 - 重構是一個細心的工作,尤其是組件的抽取、剝離,一般會分業務層、基礎層、功能層等,業務層里有各個的業務功能模塊,基礎層里有常量、巨集、工具類、分類等,功能層里是能夠抽取出來的某些具體功能方法,相同層內的不同組件不產生依賴,業務層會依賴基礎層和功能層,基礎層和功能層之間不依賴,實際工作中還需要具體情況具體對待
- 最後的主工程,需導入各個業務層里的組件,這裡組件間的通信又是討論的話題,url 、protocol、Target-Action等方案,自己也在學習和實踐階段,就不討論,本文僅記錄一些過程和問題。