讓你的SDK快速支持Pod

目錄

1 前戲
1.1 背景
1.2 回顧C(jī)ocoaPod的使用
2 讓你的SDK支持pod接入(編寫你的.podspec)
2.1 野蠻版
2.2 優(yōu)雅版
3 常見問題
3.1 SDK方面
3.1.1 如何編寫.podspec文件可以讓我的SDK在pod后呈現(xiàn)出目錄結(jié)構(gòu)?
3.1.2 SDK依賴第三方pod時(shí),針對(duì)第三方pod頭文件的包含怎樣寫?
3.1.3 SDK中的資源(圖片,配置文件等)如何調(diào)取?
3.1.4 如何驗(yàn)證我的.podspec文件?
3.1.5 pod工程有建議的模板么?
3.2 接入問題(Podfile)
3.2.1 Podfile的常見結(jié)構(gòu)是怎樣的?
3.2.2 引入一個(gè)Pod工程的Podfile配置代碼有哪些寫法?
3.2.3 Podfile常用的配置有那些?

1 前戲

1.1 背景

做過幾年開發(fā)的小伙伴都會(huì)懂得“封裝”這個(gè)詞在程序開發(fā)過程中的重要性,而極致的封裝,無非就是生產(chǎn)一個(gè)SDK(Software Development Kit)了,即將為了實(shí)現(xiàn)某一功能概念的代碼整合成一個(gè)工具包。

基于iOS,至少目前看來,集成一個(gè)SDK最理想的方式(沒有之一)就是CocoaPod了。我們當(dāng)然要想讓自己做的SDK可以支持CocoaPod,但“支持”就意味著“工作”,程序員的“工作”就意味著“踩坑”。

這篇文章即從應(yīng)用的角度教你怎樣讓你的SDK快速支持CocoaPod!并附贈(zèng)一些常見的“填坑”操作,行動(dòng)起來吧!

1.2 回顧C(jī)ocoaPod的使用

看圖,回顧下我們用爛的pod

image.png

相信Podfile大家都已經(jīng)很熟悉了,這邊我只想讓大家看到第4步【.podspec】這個(gè)文件,沒錯(cuò),它就是讓我們的SDK支持CocoaPod的關(guān)鍵所在!

Podfile:工程通過CocoaPod方式集成SDK時(shí)需要?jiǎng)?chuàng)建的文件,內(nèi)容包括:系統(tǒng)版本支持信息,pod編譯配置,具體要集成的SDK信息等。

.podspec:SDK支持CocoaPod方式接入所需要編寫的配置文件,內(nèi)容包括:SDK工程的基本信息,SDK打包的源文件、資源文件信息,SDK所依賴的庫信息,pod后的目錄結(jié)構(gòu)信息等。

2 讓你的SDK支持pod接入(編寫你的.podspec)

2.1 野蠻版

我們名為“Test”的SDK的pod支持配置文件就命名為Test.podspec,而且位置就放在Test的git工程的根目錄中好了!

image.png

野蠻版的話,Test.podspec內(nèi)容可以這樣寫:

Pod::Spec.new do |s|

  s.name         = "Test"           #必填
  s.version      = "0.0.1"          #必填
  s.summary      = ""               #非功能信息,自己玩的話,可以寫得很隨意,如:“123”
  s.description  = ""               #非功能信息,自己玩的話,可以寫得很隨意,如:“123”
  s.homepage     = ""               #非功能信息,沒啥寫的話,就填你的工程地址吧,如:"http://git.nonobank.com/yangyifan/useframeworktest"
  s.license      = "MIT"            #必填,保持"MIT"就好
  s.author       =                  #建議填寫,SDK作者,填寫如:{ "Chris" => "yangyifan@nonobank.com" }
  s.platform     = :ios             #必填,保持:ios就好(Android一般也不用pod……)
  s.ios.deployment_target = '7.0'   #必填,ios的最低系統(tǒng)版本,你可以改成"8.0"
  s.source       =                  #建議填寫,工程位置,一般如:{ :git => "git@git.nonobank.com:yangyifan/useframeworktest.git" }

  #SDK源文件,暴力方式就是將所有的源文件(比如.h.m)都在此羅列出來,不同的目錄集間使用逗號(hào)分開
  s.source_files = "Test/Classes/**/*.{h,m}","Test/Classes1/**/*.{h,m}"             

  #SDK公開頭文件,暴力方式就是將所有的頭文件(.h)都在此羅列出來,不同的目錄集間使用逗號(hào)分開
  s.public_header_files = "Test/Class/**/*.h","Test/Classes1/**/*.h"

  #[沒有就不寫]SDK的資源文件,暴力方式就是將所有的資源文件(如圖片,配置文件等)都在此羅列出來,不同的目錄集間使用逗號(hào)分開(沒有資源文件
  s.resources = "Test/Resource/TBoundle.bundle"

  #[沒有就不寫]SDK的依賴,暴力方式就是將所有的依賴都在這邊寫出來,依賴一般每個(gè)一行
  s.dependency 'AFNetworking', '>= 3.1.0'
  s.dependency 'Qiniu', '>= 7.1'

end

為什么說野蠻呢?因?yàn)樗患珊?strong>不分目錄,看不出結(jié)構(gòu)(如圖,Masonry不要打我……)

image.png

當(dāng)然,這么做無可厚非,因?yàn)镾DK的開發(fā)者沒有為使用者提供閱讀代碼方便性的顯性義務(wù)。

2.2 優(yōu)雅版

所謂優(yōu)雅,最直觀的展示便是擁有“結(jié)構(gòu)”,分清內(nèi)外(有對(duì)外頭文件)

image.png

然而,想要呈現(xiàn)出美好的結(jié)構(gòu),可不如現(xiàn)象展示上那么簡單,我們需要:
1) 思路:框架要求分清概念層次與模塊,而且下層模塊不能對(duì)上層模塊有任何依賴(存在相互依賴的狀況時(shí),針對(duì)podspec的檢查是無法通過的)

2) 過程:收集需求-->整合需求-->搭建框架-->反復(fù)“檢驗(yàn)概念&修整框架”-->確認(rèn)結(jié)構(gòu)
3) 接口:確認(rèn)你的對(duì)外頭文件,并是不SDK所有的函數(shù)、變量都期望對(duì)外公開的哦
(那樣會(huì)誤導(dǎo)使用者并讓使用者困擾也會(huì)降低SDK的健壯性

什么是分層,什么是設(shè)計(jì),下圖或許可以給大家一些參考


image.png

所以,如上圖,相比“野蠻”,所謂“優(yōu)雅”,關(guān)鍵是要在設(shè)計(jì)上花費(fèi)一些心思。更重要的,如果你的SDK會(huì)進(jìn)行版本迭代和維護(hù)的話,“野蠻”的方式最終必然會(huì)成為闌尾工程。

注:
記得一切從實(shí)際出發(fā),如果你的SDK工程只有3、5個(gè)文件,再設(shè)計(jì)數(shù)日分個(gè)3級(jí)文件目錄顯然是沒有必要的,顯然沒有哪個(gè)正常人會(huì)認(rèn)為一個(gè)文件目錄有三五個(gè)文件不是優(yōu)雅的方式。

3 常見問題

3.1 SDK方面

3.1.1 如何編寫.podspec文件可以讓我的SDK在pod后呈現(xiàn)出目錄結(jié)構(gòu)?

image.png

針對(duì)上面這張已經(jīng)第三次拋出來的圖,跟省略了細(xì)節(jié)的下面的.podspec文件代碼想比對(duì),相信你可以一一對(duì)應(yīng)起來并有一些收獲

Pod::Spec.new do |s|

  s.name         = "..."
  s.version      = "..."
  ...

  s.source_files = "MZLog/Classes/MZLog.h"
  s.public_header_files = "MZLog/Classes/MZLog.h"

  s.subspec 'Manager' do |ss|
    ss.source_files = "MZLog/Classes/Manager/**/*"
    ss.public_header_files = "MZLog/Classes/Manager/*.h"
    ss.dependency 'MZLog/AutoEvent'
    ss.dependency 'MZLog/ToolkitSenior'
    ss.dependency 'MZLog/ToolkitJunior'
    ss.dependency 'MZLog/Support'
  end

  s.subspec 'AutoEvent' do |ss|
    ...
  end

  s.subspec 'ToolkitSenior' do |ss|
    ...
  end

  s.subspec 'ToolkitJunior' do |ss|

    ss.source_files = "MZLog/Classes/ToolkitJunior/MZLogToolkitJuniorInterface.h"
    ss.public_header_files = "MZLog/Classes/ToolkitJunior/MZLogToolkitJuniorInterface.h"
    ss.dependency 'MZLog/Support'
    ss.dependency 'MZLog/ThirdPart'

    ss.subspec 'ModelTransfer' do |sss|
      ...
    end

    ss.subspec 'ModelCreater' do |sss|
      ...
    end

    ss.subspec 'Config' do |sss|
      ...
    end

    ss.subspec 'Uploaders' do |sss|
      ...      
    end

    ss.subspec 'ExtenedInfo' do |sss|
      ...
    end

    ss.subspec 'RouterManager' do |sss|
     ...
    end

    ss.subspec 'ViewPath' do |sss|
      ...
    end

    ss.subspec 'LogModels' do |sss|
      ...
    end

  end

  s.subspec 'Support' do |ss|
    ...
  end

  s.subspec 'ThirdPart' do |ss|
    ...
  end

下面是針對(duì)上面代碼的一些說明Tip:

  • 可以通過subspec關(guān)鍵字創(chuàng)建子級(jí)目錄
  • do 后面的 "ss" 就是子目錄的標(biāo)識(shí),你當(dāng)然可以任性地起別的名字
  • 每層目錄中一般都要指定source_files(源文件),public_header_files(該目錄或說模塊的對(duì)外公開頭文件)
  • source_files所參照的根目錄.podspec文件所在的目錄(如下圖)
    image.png
  • 如果你的SDK中的上層模塊要依賴下層模塊的支持,要使用.dependency關(guān)鍵字,但它后面定義的“目錄”(比如“MZLog/Support”是你的模塊名目錄,而不是物理文件目錄哦?。ㄟM(jìn)行一下關(guān)鍵字搜索,可以理解得更清晰)

3.1.2 SDK依賴第三方pod時(shí),針對(duì)第三方pod頭文件的包含怎樣寫?

針對(duì)問題場景,以AFNetworking舉例,我們常見的頭文件引入方式有:

#import <AFNetworking/AFNetworking.h>     /* 推薦?。。?/
#import <AFNetworking.h>
#import "AFNetworking.h"

這邊我們會(huì)推薦使用 #import <AFNetworking/AFNetworking.h>
原因:
1) 非“本土資源”,尖括號(hào)當(dāng)然是不二之選。
2)如果使用你SDK的工程在Podfile中加入了use_framework!選項(xiàng),那么使用#import <AFNetworking.h>會(huì)報(bào)錯(cuò)。

3.1.3 SDK中的資源(圖片,配置文件等)如何調(diào)???

理想狀況下,只要正確配飾了.podspec文件的resources,你都可以用下面的方式簡單粗暴地獲取資源文件。

[[NSBundle mainBundle] pathForResource:@"mzLogStaticConfigMap_iOS" ofType:@"json"];
[UIImage imageNamed:@"MZLogFloater.bundle/mzLogFloater_log"]
[UIImage imageNamed:@"mzLogFloater_log.png"]

但是上面方式的最大弊端就是要求:相關(guān)工程進(jìn)行pod install后,SDK的相關(guān)資源必須被加載到mainBundle中。而這個(gè)期望,在相關(guān)工程的Podfile中加入use_framework!選項(xiàng)后完全打碎……
use_framework! 讓被pod的工程以framework動(dòng)態(tài)鏈接庫的方式接入(即相關(guān)的資源被打包在了對(duì)應(yīng)的SDK的framework包中,而不在進(jìn)行pod install的工程的mainBundle中),所以,這時(shí)候,用上面的資源接入方式,SDK的資源找不到了!
解決這個(gè)問題的根本思路是找到資源所在的bundle!

/* 1. 選擇一個(gè)SDK工程中的類(因?yàn)樗欢ㄊ窃谀鉙DK的bundle中的)*/
Class curClass = NSClassFromString(@"MZLogFloaterManager");

/* 2. 獲取SDK的bundle對(duì)象 */
NSBundle *curBundle = [NSBundle bundleForClass:curClass];

/* 3. 通過pathForResource:ofType:方法找到你要的資源文件目錄 */
NSString *floaterBundlePath = [curBundle pathForResource:@"MZLogFloater" ofType:@"bundle"];

/* 4. 
 * 如果你的資源是一個(gè)圖片,直接參考下第6步 
 * 如果你的資源還是一個(gè)bundle,用bundleWithPath:獲取這個(gè)對(duì)象吧(如該行代碼)
 * 如果你的資源是一個(gè)要讀取的文件,目錄都有了你還能沒法玩兒么?(亦可以直接跳過4、5、6步)
 */
NSBundle *floaterBundle = [NSBundle bundleWithPath:floaterBundlePath];

/* 5. 跟第三步相同了,只是因?yàn)橹暗哪繕?biāo)資源還是一個(gè)bundle */
NSString *imagePath = [floaterBundle pathForResource:imageName ofType:@"png"];

/* 6. 通過imageWIthContentsOfFile:可以通過路徑獲取一個(gè)圖片的UIImage對(duì)象 */
UIImage *tmpImage = [UIImage imageWithContentsOfFile:imagePath];

3.1.4 如何驗(yàn)證我的.podspec文件?

只要在terminal中進(jìn)入.podspec文件所在的目錄,鍵入如下命令,然后就根據(jù)提示修改你的.podspec文件吧?。ㄎ也恍拍憧梢砸淮瓮P(guān)哦!warning酌情可以忽略,error“最好”解決掉)

選擇1 : pod lib lint
說明:podspec基于本地的檢查,我更常用這個(gè),畢竟將代碼推到遠(yuǎn)端前要保證你推的代碼是經(jīng)過測(cè)試的嘛!

選擇2:pod spec lint
說明:功能和pod lib lint是相似的,不過是基于遠(yuǎn)端的檢查

3.1.5 pod工程有建議的模板么?

從terminal進(jìn)入一個(gè)空的文件夾執(zhí)行下下面的代碼試試吧。

pod lib create xxxxxx
說明:xxxxxx是你的SDK工程名稱,如AFNetworking(效果如下圖)

image.png

當(dāng)然,這種結(jié)構(gòu)只是一個(gè)參考,并不是強(qiáng)制要求的。
因?yàn)楸举|(zhì)上,pod支持只是通過pod的命令找到目標(biāo)代碼、資源文件并將其打包,如果你的SDK早已有自己的結(jié)構(gòu),大可不必重新修改你自己的文件結(jié)構(gòu)

3.2 接入問題(Podfile)

3.2.1 Podfile的常見結(jié)構(gòu)是怎樣的?

直接上代碼~~

#指定工程支持的最低版本
platform :ios, '8.0'

#全局參數(shù)配置選項(xiàng)(一般不需要特殊設(shè)置)
use_frameworks!

#指定工程的Target,其內(nèi)部每行引入一個(gè)Pod工程(SDK)
target 'Test' do
    pod 'MZLog', :git => 'http://git.nonobank.com/yangyifan/mzapplog.git', :commit => '56757bb', :inhibit_warnings => true
    pod 'MJRefresh', '3.1.12'
    pod 'Test', :git => 'http://git.nonobank.com/yangyifan/useframeworktest.git', :commit => '191ad53', :inhibit_warnings => true
end

3.2.2 引入一個(gè)Pod工程的Podfile配置代碼有哪些寫法?

這邊只做常見的舉例,不保證全面,亦不做深入分析~

# 引入一個(gè)Pod工程的最新版本
pod 'SDWebImage'

# 引入一個(gè)Pod工程的指定版本
pod 'MJRefresh', '3.1.12'

# 指定工程地址和提交號(hào),引入一個(gè)Pod工程的指定版本,一般企業(yè)內(nèi)部小范圍封裝的SDK比較常用
pod 'MZLog', :git => 'http://git.nonobank.com/yangyifan/mzapplog.git', :commit => '56757bb'

3.2.3 Podfile常用的配置有那些?

#全局配置,讓被pod 的工程以framework動(dòng)態(tài)鏈接庫的方式接入,只支持iOS8.0+,含swift的工程必用該選項(xiàng)
use_frameworks!

#忽略所有pod工程的警告,絕對(duì)金牌配置!
inhibit_all_warnings!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容