最近项目按照很久之前写的CocoaPods远程私有库做代码组件化,介绍我遇到的问题。新版的Specs和之前的一样,就是目录名称改了一下,之前用s,现在用spec,其实是一样的,也可以更改其他的。之前介绍的CocoaPods远程私有库只是单独的一个.a文件和头文件,比较简单,现在要弄的是一个功能模块的组件化。这里主要是讲解.podspec文件,了解更多的.podspec文件内容,请看官网介绍:Specs and the Specs Repo。
一、添加pch文件
这个相对比较容易,添加一行 spec.prefix_header_file,如果pch文件位置改了,这里一定要更改才行,组件化里面的pch文件和项目中用到的pch文件是不冲突的,是区分开来的。
spec.prefix_header_file ='pch文件的绝对路径'
二、依赖第三方库
这个也比较简单,例如依赖SDWebImage,直接上代码:
spec.dependency "SDWebImage", "~> 4.4.6" 。
三、资源文件的存放
这个是最重要的,也是本文要讲解出现的问题,首先介绍文件的存放路径
①、spec.source_files = "" 和 spec.resources = ""。
1. 如果Podfile文件用到use_frameworks!的话(到底项目需不需要用到的话,自行网上搜索答案,这里不详细说),
所存放的文件是在库主目录下,如果库名为Libtest,那么就在Libtest.framework下,所以和平时的mainBundle不一样;
2. 如果没有用到use_frameworks!的话,所存放的文件是在主目录下,
我们平时开发的项目代码文件、资源文件(图片、plist、音视频)一般都是在mainBundle下的,代码可以直接引用。
②、spec.resource_bundles = {'Image' => ['Resources/*/.{png,jpg}']}
1. 如果Podfile文件用到use_frameworks!的话,则是存放在库主目录下(mainBundle)的Image.bundle下,
Image是随意填写的名称,如IQKeyboardManager库一样,如图所示:
2. 如果没有用到use_frameworks!的话,所存放的文件是在主目录下(mainBundle)的Image.bundle下
所以存放的路径不一样,获取就不一样,要对应获取相应的资源文件,套用网上的方法如下所示:
/**
获取文件所在name,默认情况下podName和bundlename相同,传一个即可
@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不能同时为空";
}else if (bundleName == nil ) {
bundleName = podName;
}else if (podName == nil) {
podName = bundleName;
}
if ([bundleName containsString:@".bundle"]) {
bundleName = [bundleName componentsSeparatedByString:@".bundle"].firstObject;
}
//没使用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, @"取不到关联bundle");
//生产环境直接返回空
return associateBundleURL?[NSBundle bundleWithURL:associateBundleURL]:nil;
}
我一开始不太明白资源的存放路径,走了很多弯路,折腾了两三天。我的项目用到use_frameworks!,通过综合考虑,要用到资源文件,我把所有文件都放在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上的图片不需要更改,会自动识别在当前bundle里面的图片,代码回去的image就要更改了,要用到
+ (nullable UIImage *)imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection方法
很高兴的在本地库完全没有问题,本地验证也通过了,但是远程验证没有通过(xib不能放在source_files里面 ),提交不了到pod私有库,所以有点失望。
接着我又更改了方法(把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}'],
}
此时加载xib就需要用到上面的方法
+ (NSBundle *)bundleWithBundleName:(NSString *)bundleName podName:(NSString *)podName;
这版远程验证通过,终于提交了第一版的pod私有库,但有个问题xib文件和图片不在同一个Bundle里面,所以在xib放的图片都是不能显示,看着那么多xib文件和加载图片很多,不想拉控件用代码赋值。于是就有了第三版:然后把图片放在xib同一个Bundle里面。
这时感觉没问题了,遇到的那么问题,终于可以了,心里有点小高兴,然后等到我想打包的时候,打包出错,无法打包,
这时看到的报错信息,是存放的资源文件获取不到,于是又更改存放文件。
通过多次尝试,除了.h和.m文件之外的文件都不能放在spec.source_files里面,所以只能抽出来了,
接着为了方便加载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}'
这次没有多大的问题,加载文件都是正常的,打包什么都正常,但有个问题就是远程库没有分目录,全部文件杂乱存放在一起,没眼看,但本地的有分目录很清楚,这就很无助了,然后又想着分目录。
但这分目录还是比较棘手的,我这份代码是比较多,各牵连着各,其实份目录是每个目录都可以独立或者依赖的,要不是分不了的(这里不详细说目录)。我这边只是分了资源文件和.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
就这样勉强用着,后来又增加了国际化,其实和其他文件一样存放在资源文件,最好把文件名改一下,我这里用MYLocalizable.strings讲解一下
spec..resources = 'ConnectionLib/**/*.strings'
可以定义一个宏,如果strings文件名为MYLocalizable
#define MYLocalizedString(text) \
NSLocalizedStringFromTableInBundle(text, @"MYLocalizable", [NSBundle bundleForClass:[self class]], nil)
那么就是用MYLocalizedString(text)代替NSLocalizedString(text, nil)
四、本地验证和远程验证以及上传pod库
这里用到第三方库,还有很多文件,如果用之前的简单命令是不能通过的
pod lib lint ---- 本地库验证是否有误
pod spec lint ---- 远程库验证是否有误
pod repo push Mymaster ConnectionLib.podspec ---- 提交远程私有库,
这里Mymaster是自己添加的repo名称,ConnectionLib.podspec是自己制作的podspec文件
以上都是简单的命令,在我的项目中的不能通过的,然后又加多了--allow-warnings还是不能通过,
提示:xcodebuild: Returned an unsuccessful exit code.
后来在网上查还要用到--verbose --no-clean --use-libraries才能通过
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
如果库里有依赖私有库和公有库(CocoaPods)的时候,需要在后面添加source,如:
pod lib lint --allow-warnings --verbose --no-clean --use-libraries --sources='自己私有库地址,https://github.com/CocoaPods/Specs'
pod repo push Mymaster ConnectionLib.podspec --allow-warnings --verbose --use-libraries --sources='自己私有库地址,https://github.com/CocoaPods/Specs'
五、同事使用
添加私有的repo
pod repo add Mymaster spec的存放的地址 // Mymaster名字可以随意写
然后执行 pod install 就ok了,
这里可能会有问题,地址不能用带自己用户名的地址,这个权限只有自己的权限,要输入自己的git密码,
我这边使用的是没有带用户名的地址,同事都可正常使用。(可以用https)