让你的SDK快速支持Pod

目录

1 前戏
1.1 背景
1.2 回顾CocoaPod的使用
2 让你的SDK支持pod接入(编写你的.podspec)
2.1 野蛮版
2.2 优雅版
3 常见问题
3.1 SDK方面
3.1.1 如何编写.podspec文件可以让我的SDK在pod后呈现出目录结构?
3.1.2 SDK依赖第三方pod时,针对第三方pod头文件的包含怎样写?
3.1.3 SDK中的资源(图片,配置文件等)如何调取?
3.1.4 如何验证我的.podspec文件?
3.1.5 pod工程有建议的模板么?
3.2 接入问题(Podfile)
3.2.1 Podfile的常见结构是怎样的?
3.2.2 引入一个Pod工程的Podfile配置代码有哪些写法?
3.2.3 Podfile常用的配置有那些?

1 前戏

1.1 背景

做过几年开发的小伙伴都会懂得“封装”这个词在程序开发过程中的重要性,而极致的封装,无非就是生产一个SDK(Software Development Kit)了,即将为了实现某一功能概念的代码整合成一个工具包。

基于iOS,至少目前看来,集成一个SDK最理想的方式(没有之一)就是CocoaPod了。我们当然要想让自己做的SDK可以支持CocoaPod,但“支持”就意味着“工作”,程序员的“工作”就意味着“踩坑”。

这篇文章即从应用的角度教你怎样让你的SDK快速支持CocoaPod!并附赠一些常见的“填坑”操作,行动起来吧!

1.2 回顾CocoaPod的使用

看图,回顾下我们用烂的pod

image.png

相信Podfile大家都已经很熟悉了,这边我只想让大家看到第4步【.podspec】这个文件,没错,它就是让我们的SDK支持CocoaPod的关键所在!

Podfile:工程通过CocoaPod方式集成SDK时需要创建的文件,内容包括:系统版本支持信息,pod编译配置,具体要集成的SDK信息等。

.podspec:SDK支持CocoaPod方式接入所需要编写的配置文件,内容包括:SDK工程的基本信息,SDK打包的源文件、资源文件信息,SDK所依赖的库信息,pod后的目录结构信息等。

2 让你的SDK支持pod接入(编写你的.podspec)

2.1 野蛮版

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

image.png

野蛮版的话,Test.podspec内容可以这样写:

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的最低系统版本,你可以改成"8.0"
  s.source       =                  #建议填写,工程位置,一般如:{ :git => "git@git.nonobank.com:yangyifan/useframeworktest.git" }

  #SDK源文件,暴力方式就是将所有的源文件(比如.h.m)都在此罗列出来,不同的目录集间使用逗号分开
  s.source_files = "Test/Classes/**/*.{h,m}","Test/Classes1/**/*.{h,m}"             

  #SDK公开头文件,暴力方式就是将所有的头文件(.h)都在此罗列出来,不同的目录集间使用逗号分开
  s.public_header_files = "Test/Class/**/*.h","Test/Classes1/**/*.h"

  #[没有就不写]SDK的资源文件,暴力方式就是将所有的资源文件(如图片,配置文件等)都在此罗列出来,不同的目录集间使用逗号分开(没有资源文件
  s.resources = "Test/Resource/TBoundle.bundle"

  #[没有就不写]SDK的依赖,暴力方式就是将所有的依赖都在这边写出来,依赖一般每个一行
  s.dependency 'AFNetworking', '>= 3.1.0'
  s.dependency 'Qiniu', '>= 7.1'

end

为什么说野蛮呢?因为它被集成后不分目录,看不出结构(如图,Masonry不要打我……)

image.png

当然,这么做无可厚非,因为SDK的开发者没有为使用者提供阅读代码方便性的显性义务。

2.2 优雅版

所谓优雅,最直观的展示便是拥有“结构”,分清内外(有对外头文件)

image.png

然而,想要呈现出美好的结构,可不如现象展示上那么简单,我们需要:
1) 思路:框架要求分清概念层次与模块,而且下层模块不能对上层模块有任何依赖(存在相互依赖的状况时,针对podspec的检查是无法通过的)

2) 过程:收集需求-->整合需求-->搭建框架-->反复“检验概念&修整框架”-->确认结构
3) 接口:确认你的对外头文件,并是不SDK所有的函数、变量都期望对外公开的哦
(那样会误导使用者并让使用者困扰也会降低SDK的健壮性

什么是分层,什么是设计,下图或许可以给大家一些参考


image.png

所以,如上图,相比“野蛮”,所谓“优雅”,关键是要在设计上花费一些心思。更重要的,如果你的SDK会进行版本迭代和维护的话,“野蛮”的方式最终必然会成为阑尾工程

注:
记得一切从实际出发,如果你的SDK工程只有3、5个文件,再设计数日分个3级文件目录显然是没有必要的,显然没有哪个正常人会认为一个文件目录有三五个文件不是优雅的方式。

3 常见问题

3.1 SDK方面

3.1.1 如何编写.podspec文件可以让我的SDK在pod后呈现出目录结构?

image.png

针对上面这张已经第三次抛出来的图,跟省略了细节的下面的.podspec文件代码想比对,相信你可以一一对应起来并有一些收获

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

下面是针对上面代码的一些说明Tip:

  • 可以通过subspec关键字创建子级目录
  • do 后面的 "ss" 就是子目录的标识,你当然可以任性地起别的名字
  • 每层目录中一般都要指定source_files(源文件),public_header_files(该目录或说模块的对外公开头文件)
  • source_files所参照的根目录.podspec文件所在的目录(如下图)
    image.png
  • 如果你的SDK中的上层模块要依赖下层模块的支持,要使用.dependency关键字,但它后面定义的“目录”(比如“MZLog/Support”是你的模块名目录,而不是物理文件目录哦!(进行一下关键字搜索,可以理解得更清晰)

3.1.2 SDK依赖第三方pod时,针对第三方pod头文件的包含怎样写?

针对问题场景,以AFNetworking举例,我们常见的头文件引入方式有:

#import <AFNetworking/AFNetworking.h>     /* 推荐!!!*/
#import <AFNetworking.h>
#import "AFNetworking.h"

这边我们会推荐使用 #import <AFNetworking/AFNetworking.h>
原因:
1) 非“本土资源”,尖括号当然是不二之选。
2)如果使用你SDK的工程在Podfile中加入了use_framework!选项,那么使用#import <AFNetworking.h>会报错。

3.1.3 SDK中的资源(图片,配置文件等)如何调取?

理想状况下,只要正确配饰了.podspec文件的resources,你都可以用下面的方式简单粗暴地获取资源文件。

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

但是上面方式的最大弊端就是要求:相关工程进行pod install后,SDK的相关资源必须被加载到mainBundle中。而这个期望,在相关工程的Podfile中加入use_framework!选项后完全打碎……
use_framework! 让被pod的工程以framework动态链接库的方式接入(即相关的资源被打包在了对应的SDK的framework包中,而不在进行pod install的工程的mainBundle中),所以,这时候,用上面的资源接入方式,SDK的资源找不到了!
解决这个问题的根本思路是找到资源所在的bundle

/* 1. 选择一个SDK工程中的类(因为它一定是在你SDK的bundle中的)*/
Class curClass = NSClassFromString(@"MZLogFloaterManager");

/* 2. 获取SDK的bundle对象 */
NSBundle *curBundle = [NSBundle bundleForClass:curClass];

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

/* 4. 
 * 如果你的资源是一个图片,直接参考下第6步 
 * 如果你的资源还是一个bundle,用bundleWithPath:获取这个对象吧(如该行代码)
 * 如果你的资源是一个要读取的文件,目录都有了你还能没法玩儿么?(亦可以直接跳过4、5、6步)
 */
NSBundle *floaterBundle = [NSBundle bundleWithPath:floaterBundlePath];

/* 5. 跟第三步相同了,只是因为之前的目标资源还是一个bundle */
NSString *imagePath = [floaterBundle pathForResource:imageName ofType:@"png"];

/* 6. 通过imageWIthContentsOfFile:可以通过路径获取一个图片的UIImage对象 */
UIImage *tmpImage = [UIImage imageWithContentsOfFile:imagePath];

3.1.4 如何验证我的.podspec文件?

只要在terminal中进入.podspec文件所在的目录,键入如下命令,然后就根据提示修改你的.podspec文件吧!(我不信你可以一次通关哦!warning酌情可以忽略,error“最好”解决掉)

选择1 : pod lib lint
说明:podspec基于本地的检查,我更常用这个,毕竟将代码推到远端前要保证你推的代码是经过测试的嘛!

选择2:pod spec lint
说明:功能和pod lib lint是相似的,不过是基于远端的检查

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

从terminal进入一个空的文件夹执行下下面的代码试试吧。

pod lib create xxxxxx
说明:xxxxxx是你的SDK工程名称,如AFNetworking(效果如下图)

image.png

当然,这种结构只是一个参考,并不是强制要求的。
因为本质上,pod支持只是通过pod的命令找到目标代码、资源文件并将其打包,如果你的SDK早已有自己的结构,大可不必重新修改你自己的文件结构

3.2 接入问题(Podfile)

3.2.1 Podfile的常见结构是怎样的?

直接上代码~~

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

#全局参数配置选项(一般不需要特殊设置)
use_frameworks!

#指定工程的Target,其内部每行引入一个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 引入一个Pod工程的Podfile配置代码有哪些写法?

这边只做常见的举例,不保证全面,亦不做深入分析~

# 引入一个Pod工程的最新版本
pod 'SDWebImage'

# 引入一个Pod工程的指定版本
pod 'MJRefresh', '3.1.12'

# 指定工程地址和提交号,引入一个Pod工程的指定版本,一般企业内部小范围封装的SDK比较常用
pod 'MZLog', :git => 'http://git.nonobank.com/yangyifan/mzapplog.git', :commit => '56757bb'

3.2.3 Podfile常用的配置有那些?

#全局配置,让被pod 的工程以framework动态链接库的方式接入,只支持iOS8.0+,含swift的工程必用该选项
use_frameworks!

#忽略所有pod工程的警告,绝对金牌配置!
inhibit_all_warnings!
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容