iOS SDK 开发收藏ionic

2017最新版iOS SDK制作 集成及上线攻略(非demo级

2017-09-19  本文已影响199人  阡陌不言

本文系原创,转载请注明出处,谢谢 !

前一段时间因公司业务需要,提了这样一个需求:要把一个早期的项目(创建于2013年,非本公司项目)整个做成SDK,集成到一个未知的项目里面去,而且还要上线.乍一听,这个好像也没什么难度,由于之前对静态库略有研究,于是爽快的跟老大说:能做!(当时我还没拿到源码,也不知道需求方到底想要如何对接,对于主工程更是一无所知).于是接下来两个月,本人由于自己的一时冲动,入了一个天坑,差点没爬出来.现在此项目已完结,在此把自己近2个月踩过的坑,做个总结,同时与大家分享下经验.

为了更好的梳理思路,我先在此抛出几个问题,大家一起思考:
没错,以上这些问题都遇到了,而且还不止这些.
首先,客观的说,SDK不是这么用的.

这个事情本身就是个变态的需求.其次SDK对于源码是有要求的,不是随随便便给你拉来一套代码,都能完美的把所有功能做成一个SDK,然后随便那个项目需要,就给哪个项目调(这简直万能啊,有木有) 也许有人会说了,支付宝、百度地图不就是这样吗?拜托,这些都是功能性的,没有哪个SDK是包含多个定制化界面和接口等等一堆东西的.可是,这又怎么样呢,自己挖的坑,跪着也要填完了.

前言到此结束,接下来我们言归正传.

(以上10个问题在下面都会给出答案)

(一) 方案

我创建的 SDK 叫NewCityKit,测试SDK的测试工程为 testFramework,下文中皆以此为例

SDK_2.png SDK-3.png

(二) 源码的准备

SDK-1.png SDK-4.png

(三) 资源的处理

bundle-1.png bundle-2.png bundle-3.png bundle-4.png bundle-5.png
 * 在此回答`问题5`: 图片不需要一个个修改路径,但是xib需要
iOS的资源后缀都是@2X,@3X的格式,编译成为bundle以后,会变成.tiff格式,这样你原来写的路径图片名称后面必须在再上这个后缀才能取到,这样就又麻烦了,这个问题我们可以在资源的target修改一个地方,就可以完美解决,如下图:
bundle-6.png
写 一个加载bundle的工具类

#import "BundleTools.h"
#define BUNDLE_NAME @"NewCityAsset"

@implementation BundleTools
+ (NSBundle *)getBundle{
    
    return [NSBundle bundleWithPath: [[NSBundle mainBundle] pathForResource: BUNDLE_NAME ofType: @"bundle"]];
}

+ (NSString *)getBundlePath: (NSString *) assetName{
    
    NSBundle *myBundle = [BundleTools getBundle];
    
    if (myBundle && assetName) {
        
        return [[myBundle resourcePath] stringByAppendingPathComponent: assetName];
    }
    
    return nil;
}
 

给UIImage写一个分类,用运行时替换掉系统的 imageNamed: 方法


#import "UIImage+load.h"
#import "BundleTools.h"
#import <objc/runtime.h>
@implementation UIImage (load)

+(void)load{

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
        Method m2 = class_getClassMethod([UIImage class], @selector(WB_imageNamed:));
        method_exchangeImplementations(m1, m2);
    });
}

+ (UIImage *)WB_imageNamed:(NSString *)name{

    UIImage *image = [UIImage WB_imageNamed:name];
    
    if (image) {
        return image;
    }else{
        return [UIImage imageNamed:name inBundle:[BundleTools getBundle] compatibleWithTraitCollection:nil];
    }

}

至此,图片资源处理完毕.

  
//替换前
    [self.collectionView registerNib:[UINib nibWithNibName:@"CollectionViewCell" bundle: [NSBundle mainBundle]] forCellWithReuseIdentifier:@"CollectionViewCell"];
//替换后
    [self.collectionView registerNib:[UINib nibWithNibName:@"CollectionViewCell" bundle:[BundleTools getBundle]] forCellWithReuseIdentifier:@"CollectionViewCell"];
 
#import "UIViewController+Bundle.h"
#import "BundleTools.h"
#import <objc/runtime.h>

@implementation UIViewController (Bundle)

+(void)load{
    
    Method m1 = class_getInstanceMethod([self class], @selector(init));
    Method m2 = class_getInstanceMethod([self class], @selector(v_init));
    method_exchangeImplementations(m1, m2);
    
}
- (instancetype)v_init {
    
    NSString *path = [[BundleTools getBundle] pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
    
    if (path == nil)
        return [self v_init];
    else
        return [self initWithNibName:NSStringFromClass([self class]) bundle:[BundleTools getBundle]];
    
}

至此,资源文件处理完毕,但是mainBundle的坑到此还不算完......

SDK-5.png SDK-6.png
 #define mLocalization(key, ...)     [NSString stringWithFormat: [[NSBundle mainBundle] localizedStringForKey:key value:@"" table:@"Localization"], ##__VA_ARGS__, nil]

// 注意:二进制文件的路径,从这里找 @"NewCityKit.framework" 
NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle]pathForResource:@"NewCityKit.framework" ofType:nil]];
NSString* path = [bundle pathForResource:@"APIDecryptConfig" ofType:@"plist"];

// 资源的路径,从这找 @"NewCityAsset.bundle"
NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle]pathForResource:@"NewCityAsset.bundle" ofType:nil]];
NSString* path = [bundle pathForResource:@"new_loading_black" ofType:@"gif"];

// 这个非常关键,千万不要弄混了!!!

至此,问题6告一段落

SDK-7.png

这个问题的解决方案也有2个:

SDK-8.png

 + (instancetype)mj_refreshBundle
{
    static NSBundle *refreshBundle = nil;
    if (refreshBundle == nil) {
        // 这里不使用mainBundle是为了适配pod 1.x和0.x
        refreshBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[MJRefreshComponent class]] pathForResource:@"MJRefresh" ofType:@"bundle"]];
    }
    return refreshBundle;
}

我们再来看一下这个东西最终出现在了哪里

SDK-9.png
对,是. framework!!! 而不是我们自制的.bundle大家知道怎么做了吗,原理同上,不再一一赘述
至此,bundle的坑基本罗列完毕,在我集成SDK期间,这是出bug最多的地方,也是困扰时间最长的,解决的关键就在于找对路径,参考经典库,不得不说,MJ对于bundle的处理容错性还是很高的,值得我们深究一下原理

(四) 库的兼容性问题

终于进行到这个最大的坑:问题7

关于这个我又想抛出一个问题了:
最后,关于这个问题做个小结:在SDK和主工程不是同一拨人在临近时间段内开发的情况下,除非SDK源码有时间重写或者主工程照着SDK库的版本来用,否则统一库的版本几乎是不可能的,那么这时解决兼容性问题我觉得可以用上述方法,简单而高效.
但是,SDK工程和主工程能否共用一套第三方库呢,本人没有尝试pod是否可以用于SDK工程,如果可以的话,那么这个猜想是可行的.本人认为,这是一个比较理想的集成SDK的方案,避免了库版本不统一的问题,同时大大减小了主工程安装包的大小,但是这种方案需要在客观条件允许的情况下,才能实现

(五) 打包上线的问题

最后给大家看一下SDK集成以后的文件目录:
SDK-11.png

二进制文件和资源分开的,虽然.framework里面包含了.bundle,但是主工程里2个都要拖进来,为了能正常的在主工程添加资源(这个一定要添加,否则主工程调用SDK会出错的)

SDK-13.png
至此,本人将近2个月踩过的关于制作iOS SDK的关键坑,都已经罗列完毕!!!

本人绞尽脑汁编写了将近一天的时间,如果看完对你有帮助,请点个赞!

另外,关于如何暴露接口控制器,如何调用传参等问题,本人觉得比较简单,就不细说了,不会的同学请自行搜索,也欢迎大家加我的微信,随时交流探讨

文章如有错误之处,欢迎大家批评指正,谢谢 !

上一篇 下一篇

猜你喜欢

热点阅读