入职以来一直负责SDK的开发工作。从第一个版本一直到现在,期间遇到了不少问题,踩过不少坑,也对接过不少集团内部SDK,对SDK开发也算有点微薄经验。本篇文章没啥干货,都是我在闲扯,平常上厕所时看看就可以扔了。。。
门面篇
SDK提供给别人使用的时候,第一印象很重要,直接影响到接入者对它的印象和心情。比如,我们经常听到周围同学接入一些SDK时候各种吐槽:包名和类前缀不一致,里面API命名怎么这么反人类,注释和方法对不上,回调怎么这么奇葩,怎么这么多参数设置等等。如果,我们真的接出现上面问题的SDK,我们心里肯定要咯噔下,大家第一反应不外乎以下几个单词:不靠谱,不专业,坑多。如果接入过程中,真的遇到个坑,那正好验证了之前的印象,顺便也加强下!
所以,不管里面质量咋样,这个门面很重要,一定要让接入方感觉很专业的样子。以下几个点,可以供大家参考:
- 文件组织方式清晰明了
给别人的SDK,解压下来后第一眼就能够分辨出每个文件夹是什么作用。资源文件,文档,demo,库,应该很容易进行区分。特别是资源文件,能统一用bundle进行管理的,就不要出现bundle里面有,外面也有这种情况。
- 类名前缀和包命名或者缩写要一致
接了一些SDK发现包名是xxx.framework
, 类名前缀是bbbb
, 无论是缩写还是正常思考方式都对不上。这个可以参考下系统如:UIKit
类名前缀UI
,CoreFoundation
,类名前缀CF
等等。建议包名大写字母开头,以驼峰命名方式。
- 代码风格一定要一致
SDK接入者看不到你实现上屌炸天的代码,但是接口API暴露无遗。所以,在头文件中风格一定要统一!空格,回车,函数参数大小写等细节一定要去关注。曾经接入过一个SDK,感觉每一行代码都是不同人操刀的,让我这种代码强迫症者很崩溃(好吧,不要脸一把)。
- 函数命名遵循共性,不要出现歧义或者违背大家的共识
命名这个问题,每个人都有自己的一套,个人认为只要自己保证一致,看起来也挺工整。但是一些共识大家还是需要去遵循的。比如init是用来生成对象的,不是用来设置生成好的对象属性的,这个是苹果和iOS开发一致认可的,就不要出现这种函数命名了。
- 代码注释要规范和清楚
虽然SDK提供了文档,但是接入者不一定每次都仔细去看,所以接口里面的注释还是要认认真真写。对于注释的格式,以前是喵神的VVDocumenter
,现在Xocde原生自带,正常用就好了。对于接口的入参,一定要说清楚,千万别出现说了一半,然后加了个链接:详细请参考XXXX。
- SDK功能正确,编译无警告和错误,支持最新的特性
发出去的SDK一定要经过完整测试,当然,这是废话!提供出去的SDK,应该做到无警告,配置好依赖的库后编译无错误。谁也不想见到接入SDK后工程一下子多了一堆警告。对于新的特性比如iOS的bitcode,iOS8之后开放的动态库,都应该积极去支持。
设计篇
一、易用性
SDK本身就是一些公用代码或者业务的整合,已经帮我们做了很多工作和隐藏了细节。如何让SDK易用,我觉得可以从以下几个点进行考虑:
- SDK集成成本
接入SDK步骤都是添加SDK到工程,配置好依赖库和编译设置。对于一些复杂功能的SDK或者核心部分是C或者C++实现来说配置可能更多点。还好我们有cocoapods
可以帮助我自动配置!如果能够支持cocoapods
尽量都支持吧。
- API调用简单
SDK好用不好用,看调用API就感受出来了。如果说接入方为了接一个功能,发现接入代码侵入了他们很多业务代码,那显然就是使用起来比较复杂的。开发最喜欢说的一句就是我都弄好了,你只要接下就好了,真的很简单!只要一行代码!理想情况下每个人喜欢这样的代码[PayManager payWithOrderId:@"xx"]
- 功能可以定制
上面刚说只要一行代码,这边就要写好几行代码了!实际上,我们接入SDK,可能因为API完成的功能不能细分,UI不符合项目风格和要求,某些业务流程所限等原因,我们需要对SDK进行定制来达到要求。所以,对于这点,在SDK开发的时候,SDK架构设计上要考虑这个需求。如果让接入者考虑奇淫巧技来实现这个目的,那就gg了!
- 便于调试
接入SDK肯定会遇到问题,因为SDK不是源码,所以接入者无法方便的进行调试。这个时候我们应该提供API,打印出SDK debug日志,给出一些提示性的信息,方便排查问题。
- API回调参数明确
SDK调用完,我们需要处理结果。对于回调的参数类型model
还是dictionary
。个人还是比较偏向model,为什么呢?因为model
编译的时候就可以检查而dictionary
不行。并且model
比dictionary
更清楚,知道具体的参数。这点大家可以感受下微信支付和支付宝支付的回调参数。
- API稳定
说实话,要做到这点还是有点难度的!SDK在开发前期,谁也不能保证未来API不会发生大的变动。特别是SDK业务初期快速迭代,可能每一版都会发生变化。但是,在SDK整体稳定后,大的功能API应该追求稳定。每次让接入者更新SDK和重新接入一个成本一样大,那估计得被吐槽死,以后都不想升级了。
二、API设计
个人觉得这个是SDK开发最纠结和最困难的环节!虽然我们每次都进行讨论和斟酌,有时候还是跟不上需求的变动。理想中的API设计,应该有种魔力,引导接入者去接入。或者能够和接入者开发经验或者接入其他SDK的经验产生共鸣,让他接入感觉很愉快。
下面就分享几点API设计踩过的坑和经验:
- 参数命名一定要明确无歧义
参数的命名写长点,没关系!比如platformId
和appPlatformId
。当两个参数一起传的时候,就凌乱了。这个是我们SDK初期命名踩的最坑的一个地方!接入我们SDK很多人都不懂这两个参数有啥区别。这个设计之初是没问题的,只是后来需求变了!platfomrId
真实含义是orderPlatformId
!所以一个好的命名省不少事。
- 自给自足,丰衣足食
SDK经常会采集一些接入方的数据,比如bundleID,appName,appVersion等信息。如果需要的信息能够自己获取,那就请珍惜接口每一个宝贵的入参位置。
- SDK配置参数和接口入参分开
很多SDK在使用之前,可能需要设置基本参数,比如”key“,”productId“等。基本大家都是在appDelegate中调用类似[xx registerAPP:"appid"], [xx configSDKKey:"key"]
进行初始化的。那这点为什么要说下呢?在设计SDK的初期,可能由于业务接口简单,需要的参数并不多,有时候直接当入参带进接口。如果未来入参更多,或者开放的API更多,又都用到了这个参数,并且在APP生命周期里面只要设置一次,那么可以考虑将其列为配置参数。
- SDK参数:拼接的字符串
如果入参依赖后端的配置,那么直接使用类似URL query
部分格式的字符串。比如支付宝支付的orderString: orderId=123&sign=28fhfh&sign_type=RSA
这类格式。未来扩展性比较高,而且所有版本都可以支持。
- 同一类参数,封装成
model
,隐藏属性,通过方法构造
如果API参数过多,可以考虑将一类参数归为一类,减少入参个数,也便于以后进行扩展,保持主API稳定。对于归类好的参数,建议不要让接入者通过[[xxx alloc] init]
生成对象,然后通过setter
进行参数赋值。直接隐藏掉类属性,通过提供的便捷构造方法进行创建和赋值。这样做的好处是,在API参数升级的时候,如果新增的参数必传,可以在接入方升级的时候,只要需要改动参数构造的API。
- API功能单一,减少类似
enum
的入参设计
一个API应该单一,入参中除特殊情况外,都不应该出现类似type
,enum
等入参形式。这类信息最好对接入者屏蔽。一个API承担的功能越多,未来改动的可能性越大。
- 用于查询的属性,绝对不能直接设置
SDK会提供一些方法和属性,让接入者知道SDK的当前状态。常见的比如- (BOOL)islogin;
,@property(nonatomic, assign, readonly) BOOL isLogin;
。方法可以隐藏属性,保证不被修改,如果是属性,一定要加readonly
。
- API 回调设计
回调设计其实没啥好说的,delegate和block比较常见。根据场景自己决定一种。
开发篇
一、开发工具
对于iOS来说,就是个Xcode,但是考虑成工程搭建和打包的方便建议使用cocoapods和cocoapods-packager。
搭建开发工程:
pod lib create xxx
打包:
pod package xxx.spec
完美配合!
二、开发和测试工程
SDK寄生在APP中,开发者需要搭建测试工程,去开发和模拟接入的真实情况。这边想说的就是开发一个测试工程,方便的进行测试和开发,工作量其实并不小。SDK里面能不写测试代码的都不要去写,写得越多,越容易出现发版没有关闭测试代码的情况。得益于OC是门动态语言,很多工具都可以以AOP的形式进行集成。
分享下我们SDK主要开发了哪些功能:
- 网络环境自由切换,本地MOCK和远程一键切换
- 日志系统,自由筛选不同日志
- 测试账号一键切换
- crash日志本地收集
- 第三方调试工具:FLEX
- 测试环境数据一键录入
- 单元测试BDD,UI自动化
以上这些功能不仅可以方便开发和调试,也提高了测试同学的效率。
三、开发注意事项
由于SDK开发,很多东西没有开发APP那么爽,缺少了优秀的第三方,很多东西都要自己去造轮子!而且运行环境很恶劣!你不知道外面有多少黑科技在搞着你。
开发SDK的人可以注意下以下几个点:
- 能用系统的API解决的,就不要使用第三方,减少对其他库的依赖
- OC没有命名空间,类命名和类别方法加上前缀
- 黑科技虽然好,但是能不用的就不要用
- 多考虑第三方带来的影响,比如键盘处理,UIKit的UIAppearance等
- 依赖其他SDK的,别打包在一起,不然出现符号表重复
- 使用了OC类别打包的时候记得加上-ObjC
- 能不用单例的就尽量少的使用
- 核心代码的安全性
- 资源文件使用bundle进行管理,能不用xib的就别用了吧
发货篇
万事俱备,就差包装好发货了。
-
SDK打包形式
- Static Library
- Dynamic Framework
- Universal Framework
版本控制
(major).(minor).(patch) 如3.8.1
-
发布渠道
- cocoapods
- 邮件
- 网站
-
文档
- 千万别word!!!
- 图文并茂,依赖配置一定要注明清楚
-
demo
- 复制demo代码,就可以完成接入了!!
-
接入帮助
- 技术支持联系方式
- 接入群
- GitHub issues