iOS元件化之私有庫

RunTitan發表於2018-08-30
  • 隨著公司業務的不斷髮展,應用的程式碼體積將會越來越大,業務程式碼耦合也越來越多,程式碼量也是急劇增加
  • 如果僅僅完成程式碼拆分還不足以解決業務之間的程式碼耦合,而元件化是一種能夠解決程式碼耦合、業務工程能夠獨立執行的技術
  • 這篇文章主要介紹遠端私有庫的建立和管理以及本地索引庫的使用, 並且可參照釋出開源框架到CocoaPods入坑指南
  • 個人原文部落格地址: iOS元件化之私有庫

本地庫方案

建立本地私有庫

  • 首先需要一個宿主工程MainMoudle和一個用於存放所有本地私有庫的資料夾AllMoudles, 這兩個資料夾在同一目錄下
  • AllMoudles資料夾中建立一個私有庫TitanFMBase, 在子目錄建立Classes用於存放所有的檔案, 目錄如下: AllMoudles/TitanFMBase/Classes
  • Classes資料夾中新增檔案, 並提交到本地git
//進入TitanFMBase資料夾
cd xxx/AllMoudles/TitanFMBase

//初始化git
git init

//將原生程式碼提交到本地倉庫
git add .

// 提交修改到本地倉庫
git commit -m '你的修改記錄'

//建立spec檔案
pod spec cteate TitanFMBase
複製程式碼

最後開啟TitanFMBase資料夾中的TitanFMBase.podspec, 修改對應的配置資訊, 可參考修改部落格

注意點

source配置中, 本地庫的git地址不需要填

s.source = { :git => "", :tag => "#{s.version}" }
複製程式碼

使用本地私有庫

  • 安裝和使用本地私有庫和和遠端私有庫步驟一樣, 不同的是:
    • 遠端私有庫不需要指明庫的地址
    • 本地私有庫需要制定庫地址(相對路徑即可)
  • 使用path的形式新增框架依賴
pod 'TitanFMBase', :path => '../AllMoudles/TitanFMBase'
複製程式碼

注意點

  • 入夥時本地私有庫, 不需要使用pod lib lint或者pod spec lint驗證spec檔案的正確性
  • 因為有些欄位只有遠端私有庫才需要設定, 只要保證本地私有庫的路徑正確, 並不影響使用

遠端私有庫

  • 當我們在終端執行pod search命令時, 搜尋的其實是本地快取的spec檔案, 當然第一次使用時需要先更新本地的spec檔案
  • 可在終端執行pod repo命令檢視當前本地的索引庫, 或者檢視目錄~/.cocoapods/repos/master/Specs
  • 在元件化開發過程中, 為防止程式碼洩露, 我們必須要建立自己的本地索引庫
  • 釋出開源框架到CocoaPods入坑指南文章中提到了一種建立方式
    • 但是上述提到的建立方式, 需要手動建立podspec檔案
    • 需要手動進行git管理, 不能測試, 需要手動新增測試工程
  • 執行pod repo add TitanSpec http://xxxx命令, 可建立一個新的本地索引庫
  • 建立遠端索引庫, 碼市
  • 除圖中紅色箭頭外, 其他地方不需要填寫, 新建即可

CreateSpec

建立遠端私有庫

  • pod lib create xxx
  • 規範建立私有庫, 執行上述命令, 可同時建立spec檔案和測試工程等
  • 根據提示建立不同的配置檔案即可, 建立完成後, 將需要新增的私有庫檔案放到xxx/xxx/Classes資料夾下即可, 預設建立的.m檔案可刪除
  • 最後需要開啟Example目錄下的測試工程, 並執行pod install命令, 將你的私有庫檔案安裝到測試工程
  • 最後修改xxx檔案下的xxx.podspec檔案中相關配置即可

createLib

做完上述工作即可將專案所有檔案提交到遠端私有庫了

// 將原生程式碼加入本地倉庫裡
git add .

// 提交修改到本地倉庫
git commit -m '你的修改記錄'

// 檢視當前的遠端連線
git remote
// 新增名稱為origin的遠端連線
git remote add origin '你的github專案地址'

// 在push之前, 檢視spec是否配置有問題
// 驗證本地spec檔案是否有誤
pod lib lint
// 驗證遠端spec檔案是否有誤
pod spec lint

// 推送master分支的程式碼到名稱為origin的遠端倉庫
git push origin master
複製程式碼
  • 正常情況下本地驗證一般沒問題, 遠端驗證正常情況下會有問題
  • 本地驗證不會驗證s.source後面的tag
  • 遠端驗證會驗證tag, 而至此我們的tag還沒有設定, 所以驗證不會通過, 需要打標籤
  • 設定好tag, 再次驗證應該就是沒問題的了
// 檢視當前的tag值
git tag

// 設定tag值
git tag "0.0.1"  

// 上傳提交tag
git push --tags


// 刪除標籤相關命令
// 先刪除本地再刪除遠端標籤, 刪除後需要重新打標籤
// 刪除本地標籤
git tag -d 0.0.1

// 刪除遠端標籤
git push origin :0.0.1

複製程式碼

提交私有的SpecRepo

向私有的SpecRepo中提交podspec:

pod repo push SpecName XXX.podspec
複製程式碼

注意點

  • 提交podspec的過程中會有驗證, 最好在提交之前先驗證spec檔案的配置是有問題pod lib lint
    • 警告可以使用--allow-warings忽略
    • 但是涉及到的錯誤資訊一定要解決
  • 提交過程中, 會提交資訊到遠端私有索引庫

使用私有庫

  • 檢索私有庫: pod search XXX
    • 如果檢索不到, 可以先到私有索引庫內, 看看是否存在私有庫
    • 如果存在還是檢索不到, 則直接刪除私有庫索引檔案, 重新配置
  • Podfile檔案中, 同事使用私有庫和第三方庫是需要指定對應的source
  • pod repo命令執行後的結果
master
- Type: git (master)
- URL:  'https://github.com/CocoaPods/Specs.git'
- Path: /Users/xxx/.cocoapods/repos/master

TitanFMSpec
- Type: git (master)
- URL:  'https://git.coding.net/CoderTitan/TitanFMSpec.git'
- Path: /Users/xxx/.cocoapods/repos/TitanFMSpec
複製程式碼

Podfile檔案中配置資訊

// 遠端私有庫
source 'https://git.coding.net/CoderTitan/TitanFMSpec.git'
// 官方倉庫
source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '8.0'

target 'TitanjunFM' do
  use_frameworks!

pod 'TitanFMBase'
pod 'MJExtension'

end
複製程式碼

更新私有庫

更新遠端私有庫

  1. 修改xxx/xxx/Classes資料夾下對應的庫檔案
  2. 更新測試工程的Pod庫檔案: pod update --no-repo-update
  3. 更新xxx.podspec檔案的配置資訊, 版本號一定要改
  4. 提交程式碼到遠端倉庫: git push origin master
  5. 更新tag標籤: git push --tags
  6. 更新遠端和本地的私有索引庫: pod repo push SpecName XXX.podspec

私有庫依賴

初步設計方案

  • 在設計私有庫的過程中難免可能會涉及到使用其他第三方庫的情況, 又該如何解決這種問題
  • 新增元件依賴: 在podspec檔案配置中, 新增如下依賴程式碼
s.dependency 'AFNetworking'
s.dependency 'SDWebImage'
複製程式碼
  • 注意依賴, 以及框架標頭檔案中, 不要直接匯入依賴框架的標頭檔案
  • 也就是說, 上述涉及到的所依賴的第三方庫, 在專案的Podfile檔案中, 不會再匯入該類庫

優化方案

上述方案存在的問題: 假如另外一個業務線, 僅僅需要依賴一些基礎配置, 但是, 如果把整個庫作為依賴, 便會匯入一些不用的冗餘程式碼

  • 這樣, 我們現在終端執行pod search AFNetworking, 看一下AFNetworking的搜尋結果
  • 會看到Subspecs中, 將AFNetworking分成了幾個不同的部分, 這樣我們就可以根據不同的功能需求匯入不同部分的程式碼即可, 防止程式碼冗餘
-> AFNetworking (3.2.1)
   A delightful iOS and OS X networking framework.
   pod 'AFNetworking', '~> 3.2.1'
   - Homepage: https://github.com/AFNetworking/AFNetworking
   - Source:   https://github.com/AFNetworking/AFNetworking.git
   - Versions: 3.2.1, ......,0.5.1 [master repo]
   - Subspecs:
     - AFNetworking/Serialization (3.2.1)
     - AFNetworking/Security (3.2.1)
     - AFNetworking/Reachability (3.2.1)
     - AFNetworking/NSURLSession (3.2.1)
     - AFNetworking/UIKit (3.2.1)
複製程式碼

為解決將私有庫中的程式碼分成不同的功能模組, 使用subspec語法配置podspec檔案, 如下:

//格式:
s.subspec 'XXX' do |x|
    //需要匯入的所有檔案的相對路徑
    x.source_files = '相對路徑/**/*'
    //需要匯入的.h標頭檔案的相對路徑
    x.public_header_files = '相對路徑/**/*.h'
    //需要匯入的資原始檔的相對路徑
    x.resource = "相對路徑/**/*.{bundle,nib,xib}"
    //所依賴的其他的庫
    x.dependency 'AFNetworking', '~> 1.0.0'
end

//示例:
s.subspec 'Network' do |n|
    n.source_files = 'TitanFMBase/Classes/Network/**/*'
    n.dependency 'AFNetworking'
end
複製程式碼
  • 將原來的s.source_files改成上述語法即可
  • 外部使用時只需匯入pod 'AFNetworking/Reachability'即可

私有庫的資源引用

xib&storyboard

  • 所有私有庫中的xib必須動態獲取
  • 私有庫中引用圖片資源或者Xib資源時, 又該如何引用呢?
  • 都知道專案中引用Xib時, 通常方式是[[NSBundle mainBundle] load]方式, 但是這種方式在私有庫中顯然不適用
  • 在私有庫中載入私有庫中的XIb, 使用方法[NSBundle bundleForClass:self]動態獲取, 具體看一下
// MiddleView.m
    
NSBundle *mainBundle = [NSBundle mainBundle];
NSBundle *bundle = [NSBundle bundleForClass:self];
    
MiddleView *middleView = [[bundle loadNibNamed:@"MiddleView" owner:nil options:nil] firstObject];

// 列印一下上述兩個bundle如下:
// mainBundle:
NSBundle </Users/xxx/Library/Developer/CoreSimulator/Devices/6B74958F-560F-4BF4-9BDF-9AD789379FC9/data/Containers/Bundle/Application/FC9747F0-8A82-4643-AC7E-BDC268190B8D/TitanFM.app>
// bundle:
NSBundle </Users/xxx/Library/Developer/CoreSimulator/Devices/6B74958F-560F-4BF4-9BDF-9AD789379FC9/data/Containers/Bundle/Application/FC9747F0-8A82-4643-AC7E-BDC268190B8D/TitanFM.app/Frameworks/TitanFMMain.framework>
複製程式碼
  • 從上述資訊中可以看到, 專案中正常的Xib等資原始檔是放在TitanFM.app中的
  • 而私有庫的Xib等資原始檔是放在TitanFM.app/Frameworks/TitanFMMain.framework檔案目錄下的, 所以私有庫中的資原始檔載入, 要到對應的檔案目錄下
  • 具體也可以到TitanFM.app中檢視, 找到對應的app檔案, 顯示包內容, 即可層級檢視

圖片資源

圖片存放問題

  • 正常專案中, 我們的圖片一般都會放在類似字尾.xcassets的檔案中
  • 在設計私有庫時, 在Classes的同級目錄中會預設建立一個Assets的資料夾, 用於存放圖片等資源
  • podspec檔案中, 同樣修改載入檔案資源的配置, 如下:
s.resource_bundles = {
   'MainMoudle' => ['MainMoudle/Assets/*']
}
複製程式碼

修改完配置資訊和圖片記得執行pod install把資原始檔匯入到專案中

私有庫圖片的使用

xib中載入圖片, 需要在圖片前面加上元件的主bundle, 類似: MainMoudle.bundle/tabbat_back

xibimage

私有庫中使用程式碼載入圖片, 一定不能使用imageNamed方法

//1. 獲取當前的bundleName
NSBundle *currentBundle = [NSBundle bundleForClass:[self class]];

//2. 根據圖片名稱, 在bundle中檢索圖片路徑
NSString *path = [currentBundle pathForResource:@"tabbar_np_play@2x.png" ofType:nil inDirectory:@"MainMoudle.bundle"];

//獲取圖片
UIImage *image = [UIImage imageWithContentsOfFile:path];
複製程式碼

引用圖片需要注意的的是

  • 圖片引用過程中不會自動選擇@2x和@3x的圖片, 所以必須手動指定具體的圖片名稱包括圖片字尾名
  • 獲取路徑的方法pathForResource, 也要必須指明圖片所在的bundle路徑, 即inDirectory引數不可為空

提交本地的私有庫索引

  • 當你的私有庫中引用了其他的私有庫框架, 比如MainMoudle中引用了TitanFMBase/Category部分
  • 則切記不要進行本地和遠端的spec驗證, 否則可能回報錯, 原因只是因為spec中預設的依賴庫是共有的索引庫, 私有庫無法檢索到, 錯誤資訊如下圖
  • 提交本地索引的過程中遇到類似錯誤則可以, 直接忽略提交即可, 但是其他的錯誤資訊, 切記需要修改好, 警告可以忽略, 但是錯誤資訊不能忽略(依賴私有庫的問題除外)
// 提交本地私有索引庫需要忽略警告的命令
pod repo push TitanjunSpec MainMoudle.podspec --allow-warnings
複製程式碼

podspec

相關參考


相關文章