最近項(xiàng)目按照很久之前寫的CocoaPods遠(yuǎn)程私有庫(kù)做代碼組件化,介紹我遇到的問(wèn)題。新版的Specs和之前的一樣,就是目錄名稱改了一下,之前用s,現(xiàn)在用spec,其實(shí)是一樣的,也可以更改其他的。之前介紹的CocoaPods遠(yuǎn)程私有庫(kù)只是單獨(dú)的一個(gè).a文件和頭文件,比較簡(jiǎn)單,現(xiàn)在要弄的是一個(gè)功能模塊的組件化。這里主要是講解.podspec文件,了解更多的.podspec文件內(nèi)容,請(qǐng)看官網(wǎng)介紹:Specs and the Specs Repo。
一、添加pch文件
這個(gè)相對(duì)比較容易,添加一行 spec.prefix_header_file,如果pch文件位置改了,這里一定要更改才行,組件化里面的pch文件和項(xiàng)目中用到的pch文件是不沖突的,是區(qū)分開(kāi)來(lái)的。
spec.prefix_header_file ='pch文件的絕對(duì)路徑'
二、依賴第三方庫(kù)
這個(gè)也比較簡(jiǎn)單,例如依賴SDWebImage,直接上代碼:
spec.dependency "SDWebImage", "~> 4.4.6" 。
三、資源文件的存放
這個(gè)是最重要的,也是本文要講解出現(xiàn)的問(wèn)題,首先介紹文件的存放路徑
①、spec.source_files = "" 和 spec.resources = ""。
1. 如果Podfile文件用到use_frameworks!的話(到底項(xiàng)目需不需要用到的話,自行網(wǎng)上搜索答案,這里不詳細(xì)說(shuō)),
所存放的文件是在庫(kù)主目錄下,如果庫(kù)名為L(zhǎng)ibtest,那么就在Libtest.framework下,所以和平時(shí)的mainBundle不一樣;
2. 如果沒(méi)有用到use_frameworks!的話,所存放的文件是在主目錄下,
我們平時(shí)開(kāi)發(fā)的項(xiàng)目代碼文件、資源文件(圖片、plist、音視頻)一般都是在mainBundle下的,代碼可以直接引用。
②、spec.resource_bundles = {'Image' => ['Resources/*/.{png,jpg}']}
1. 如果Podfile文件用到use_frameworks!的話,則是存放在庫(kù)主目錄下(mainBundle)的Image.bundle下,
Image是隨意填寫的名稱,如IQKeyboardManager庫(kù)一樣,如圖所示:

2. 如果沒(méi)有用到use_frameworks!的話,所存放的文件是在主目錄下(mainBundle)的Image.bundle下
所以存放的路徑不一樣,獲取就不一樣,要對(duì)應(yīng)獲取相應(yīng)的資源文件,套用網(wǎng)上的方法如下所示:
/**
獲取文件所在name,默認(rèn)情況下podName和bundlename相同,傳一個(gè)即可
@param bundleName bundle名字,就是在resource_bundles里面的名字
@param podName pod的名字
@return bundle
*/
+ (NSBundle *)bundleWithBundleName:(NSString *)bundleName podName:(NSString *)podName{
if (bundleName == nil && podName == nil) {
@throw @"bundleName和podName不能同時(shí)為空";
}else if (bundleName == nil ) {
bundleName = podName;
}else if (podName == nil) {
podName = bundleName;
}
if ([bundleName containsString:@".bundle"]) {
bundleName = [bundleName componentsSeparatedByString:@".bundle"].firstObject;
}
//沒(méi)使用framwork的情況下
NSURL *associateBundleURL = [[NSBundle mainBundle] URLForResource:bundleName withExtension:@"bundle"];
//使用framework形式
if (!associateBundleURL) {
associateBundleURL = [[NSBundle mainBundle] URLForResource:@"Frameworks" withExtension:nil];
associateBundleURL = [associateBundleURL URLByAppendingPathComponent:podName];
associateBundleURL = [associateBundleURL URLByAppendingPathExtension:@"framework"];
NSBundle *associateBunle = [NSBundle bundleWithURL:associateBundleURL];
associateBundleURL = [associateBunle URLForResource:bundleName withExtension:@"bundle"];
}
NSAssert(associateBundleURL, @"取不到關(guān)聯(lián)bundle");
//生產(chǎn)環(huán)境直接返回空
return associateBundleURL?[NSBundle bundleWithURL:associateBundleURL]:nil;
}
我一開(kāi)始不太明白資源的存放路徑,走了很多彎路,折騰了兩三天。我的項(xiàng)目用到use_frameworks!,通過(guò)綜合考慮,要用到資源文件,我把所有文件都放在Classes目錄下,我的第一版是這樣寫的,如下:
spec.source_files = "ConnectionLib/Classes", "ConnectionLib/Classes/**/*.{h,m,xib,storyboard,plist,png}"
spec.resource_bundles = {
'SAJConnectVoice' => ['ConnectionLib/Classes/**/*.wav'],
'SAJConnectHtml' => ['ConnectionLib/Classes/**/*.{html,js,css}'],
}
加載xib、storyboard和plist文件用[NSBundle bundleForClass:[self class]],不能用[NSBundle mainBundle],
加載.wav和.html用到上面的方法+ (NSBundle *)bundleWithBundleName:(NSString *)bundleName podName:(NSString *)podName;
在xib上的圖片不需要更改,會(huì)自動(dòng)識(shí)別在當(dāng)前bundle里面的圖片,代碼回去的image就要更改了,要用到
+ (nullable UIImage *)imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection方法
很高興的在本地庫(kù)完全沒(méi)有問(wèn)題,本地驗(yàn)證也通過(guò)了,但是遠(yuǎn)程驗(yàn)證沒(méi)有通過(guò)(xib不能放在source_files里面 ),提交不了到pod私有庫(kù),所以有點(diǎn)失望。
接著我又更改了方法(把xib文件放在resource_bundles),那就是的第二版:
spec.source_files = "ConnectionLib/Classes", "ConnectionLib/Classes/**/*.{h,m,storyboard,plist,png}"
spec.resource_bundles = {
'SAJConnectXib' => ['ConnectionLib/Classes/**/*.xib'],
'SAJConnectVoice' => ['ConnectionLib/Classes/**/*.wav'],
'SAJConnectHtml' => ['ConnectionLib/Classes/**/*.{html,js,css}'],
}
此時(shí)加載xib就需要用到上面的方法
+ (NSBundle *)bundleWithBundleName:(NSString *)bundleName podName:(NSString *)podName;
這版遠(yuǎn)程驗(yàn)證通過(guò),終于提交了第一版的pod私有庫(kù),但有個(gè)問(wèn)題xib文件和圖片不在同一個(gè)Bundle里面,所以在xib放的圖片都是不能顯示,看著那么多xib文件和加載圖片很多,不想拉控件用代碼賦值。于是就有了第三版:然后把圖片放在xib同一個(gè)Bundle里面。
這時(shí)感覺(jué)沒(méi)問(wèn)題了,遇到的那么問(wèn)題,終于可以了,心里有點(diǎn)小高興,然后等到我想打包的時(shí)候,打包出錯(cuò),無(wú)法打包,
這時(shí)看到的報(bào)錯(cuò)信息,是存放的資源文件獲取不到,于是又更改存放文件。
通過(guò)多次嘗試,除了.h和.m文件之外的文件都不能放在spec.source_files里面,所以只能抽出來(lái)了,
接著為了方便加載xib、storyboard、plist、png等,于是我把它們放在spec.resources里面。
這跟放在spec.source_files里面是一樣的,存放的目錄都一樣的。
spec.source_files = 'ConnectionLib/Classes/**/*.{h,m}'
spec.resources = 'ConnectionLib/**/*.{storyboard,plist,xib,png,wav,html,js,css}'
這次沒(méi)有多大的問(wèn)題,加載文件都是正常的,打包什么都正常,但有個(gè)問(wèn)題就是遠(yuǎn)程庫(kù)沒(méi)有分目錄,全部文件雜亂存放在一起,沒(méi)眼看,但本地的有分目錄很清楚,這就很無(wú)助了,然后又想著分目錄。
但這分目錄還是比較棘手的,我這份代碼是比較多,各牽連著各,其實(shí)份目錄是每個(gè)目錄都可以獨(dú)立或者依賴的,要不是分不了的(這里不詳細(xì)說(shuō)目錄)。我這邊只是分了資源文件和.h和.m直接的
spec.subspec 'Classes' do |classes|
classes.source_files = 'ConnectionLib/Classes/**/*.{h,m}'
end
spec.subspec 'Resources' do |resources|
resources.subspec 'Image' do |image|
image.resources = 'ConnectionLib/**/*.png'
end
resources.subspec 'Storyboard' do |storyboard|
storyboard.resources = 'ConnectionLib/**/*.storyboard'
end
resources.subspec 'Xib' do |xib|
xib.resources = 'ConnectionLib/**/*.xib'
end
resources.subspec 'Voice' do |voice|
voice.resources = 'ConnectionLib/**/*.wav'
end
resources.subspec 'Html' do |html|
html.resources = 'ConnectionLib/**/*.{html,js,css}'
end
resources.subspec 'Plist' do |plist|
plist.resources = 'ConnectionLib/**/*.plist'
end
resources.subspec 'Localizable' do |localizable|
localizable.resources = 'ConnectionLib/**/*.strings'
end
end
就這樣勉強(qiáng)用著,后來(lái)又增加了國(guó)際化,其實(shí)和其他文件一樣存放在資源文件,最好把文件名改一下,我這里用MYLocalizable.strings講解一下
spec..resources = 'ConnectionLib/**/*.strings'
可以定義一個(gè)宏,如果strings文件名為MYLocalizable
#define MYLocalizedString(text) \
NSLocalizedStringFromTableInBundle(text, @"MYLocalizable", [NSBundle bundleForClass:[self class]], nil)
那么就是用MYLocalizedString(text)代替NSLocalizedString(text, nil)
四、本地驗(yàn)證和遠(yuǎn)程驗(yàn)證以及上傳pod庫(kù)
這里用到第三方庫(kù),還有很多文件,如果用之前的簡(jiǎn)單命令是不能通過(guò)的
pod lib lint ---- 本地庫(kù)驗(yàn)證是否有誤
pod spec lint ---- 遠(yuǎn)程庫(kù)驗(yàn)證是否有誤
pod repo push Mymaster ConnectionLib.podspec ---- 提交遠(yuǎn)程私有庫(kù),
這里Mymaster是自己添加的repo名稱,ConnectionLib.podspec是自己制作的podspec文件
以上都是簡(jiǎn)單的命令,在我的項(xiàng)目中的不能通過(guò)的,然后又加多了--allow-warnings還是不能通過(guò),
提示:xcodebuild: Returned an unsuccessful exit code.
后來(lái)在網(wǎng)上查還要用到--verbose --no-clean --use-libraries才能通過(guò)
pod lib lint --allow-warnings --verbose --no-clean --use-libraries
pod spec lint --allow-warnings --verbose --no-clean --use-libraries
pod repo push Mymaster ConnectionLib.podspec --allow-warnings --verbose --use-libraries
如果庫(kù)里有依賴私有庫(kù)和公有庫(kù)(CocoaPods)的時(shí)候,需要在后面添加source,如:
pod lib lint --allow-warnings --verbose --no-clean --use-libraries --sources='自己私有庫(kù)地址,https://github.com/CocoaPods/Specs'
pod repo push Mymaster ConnectionLib.podspec --allow-warnings --verbose --use-libraries --sources='自己私有庫(kù)地址,https://github.com/CocoaPods/Specs'
五、同事使用
添加私有的repo
pod repo add Mymaster spec的存放的地址 // Mymaster名字可以隨意寫
然后執(zhí)行 pod install 就ok了,
這里可能會(huì)有問(wèn)題,地址不能用帶自己用戶名的地址,這個(gè)權(quán)限只有自己的權(quán)限,要輸入自己的git密碼,
我這邊使用的是沒(méi)有帶用戶名的地址,同事都可正常使用。(可以用https)
