用CocoaPods管理代码组件化遇到的问题

最近项目按照很久之前写的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库一样,如图所示:
image
    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)
image.png

你可能感兴趣的:(用CocoaPods管理代码组件化遇到的问题)