[JiaJung]&iOS组件化(上篇)- 拆分基础组件
修改说明:有朋友反映Xcode9编译RAC4.x失败,无法设置Use Legacy Swift Language Version 的问题。现在将RAC版本改成了2.5(OC版本)。感谢朋友们的反馈。git clone 之后执行pod install即可。不用再设置UseLegacySwiftLanguage Version。
组件化在业界已经炒的水深火热,关于组件化的好处和组件化的方案网上已经有大篇的文章了。(Casa的这篇文章对几种方案的利弊讲述的很不错)笔者就不在赘述了。笔者通过拆分一个现有的demo来简单聊一下项目实施组件化的过程(将分为上篇、中篇、下三篇)。
## 一、Demo简要说明
demo可以从github下载(下载之后执行pod install ), 因为之前用RAC+MVVM写项目,这里用了RAC4.x。pod install之后需要修改一下ReactiveCocoa和Result的UseLegacySwiftLanguage Version 为YES。按照下面的方式解决即可:


设置好之后编译运行就ok了。(接口不太稳定,第一次运行可能没数据,再运行一次就ok了)
demo采用了“去model化”的设计,关于“去model化”的优势《去model化和数据对象》讲述的很清楚。
以下内容摘自《iOS应用架构谈 网络层设计方案》
交付什么样的数据给业务层?
我见过非常多的App的网络层在拿到JSON数据之后,会将数据转变成对应的对象原型。注意,我这里指的不是NSDictionary,而是类似Item这样的对象。这种做法是能够提高后续操作代码的可读性的。在比较直觉的思路里面,是需要这部分转化过程的,但这部分转化过程的成本是很大的,主要成本在于:
数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。
调试时通过对象原型查看数据内容不如直接通过NSDictionary/NSArray直观。
同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方。
其实我们的理想情况是希望API的数据下发之后就能够直接被View所展示。首先要说的是,这种情况非常少。另外,这种做法使得View和API联系紧密,也是我们不希望发生的。
在设计安居客的网络层数据交付这部分时,我添加了reformer(名字而已,叫什么都好)这个对象用于封装数据转化的逻辑,这个对象是一个独立对象,事实上,它是作为Adaptor模式存在的。我们可以这么理解:想象一下我们洗澡时候使用的莲蓬头,水管里出来的水是API下发的原始数据。reformer就是莲蓬头上的不同水流挡板,需要什么模式,就拨到什么模式。
以下内容摘自《iOS应用架构谈 组件化方案》
组件化方案中的去model设计
组件间调用时,是需要针对参数做去model化的。如果组件间调用不对参数做去model化的设计,就会导致业务形式上被组件化了,实质上依然没有被独立。
假设模块A和模块B之间采用model化的方案去调用,那么调用方法时传递的参数就会是一个对象。
如果对象不是一个面向接口的通用对象,那么mediator的参数处理就会非常复杂,因为要区分不同的对象类型。如果mediator不处理参数,直接将对象以范型的方式转交给模块B,那么模块B必然要包含对象类型的声明。假设对象声明放在模块A,那么B和A之间的组件化只是个形式主义。如果对象类型声明放在mediator,那么对于B而言,就不得不依赖mediator。但是,大家可以从上面的架构图中看到,对于响应请求的模块而言,依赖mediator并不是必要条件,因此这种依赖是完全不需要的,这种依赖的存在对于架构整体而言,是一种污染。
如果参数是一个面向接口的对象,那么mediator对于这种参数的处理其实就没必要了,更多的是直接转给响应方的模块。而且接口的定义就不可能放在发起方的模块中了,只能放在mediator中。响应方如果要完成响应,就也必须要依赖mediator,然而前面我已经说过,响应方对于mediator的依赖是不必要的,因此参数其实也并不适合以面向接口的对象的方式去传递。
因此,使用对象化的参数无论是否面向接口,带来的结果就是业务模块形式上是被组件化了,但实质上依然没有被独立。
在这种跨模块场景中,参数最好还是以“去model化”的方式去传递,在iOS的开发中,就是以字典的方式去传递。这样就能够做到只有调用方依赖mediator,而响应方不需要依赖mediator。然而在去model化的实践中,由于这种方式自由度太大,我们至少需要保证调用方生成的参数能够被响应方理解,然而在组件化场景中,限制去model化方案的自由度的手段,相比于网络层和持久层更加容易得多。
因为组件化天然具备了限制手段:参数不对就无法调用!无法调用时直接debug就能很快找到原因。
本文中demo的大致结构如下:

将一个项目组件化拆分掉,一般会拆分一些基础组件、一些功能组件和业务组件。将拆分好的组件放到远程仓库,统一通过Cocoapods进行管理。
## 二、 几个基本概念
当然,要实现这个管理的过程,有一些概念还是必须知道的。

如上图所示: 远程索引库、本地索引库、远程代码库、本地代码库。笔者通过拆分demo 中的一个category的基础组件说明上面的四个概念。
## 三、实践
第一步:基础组件Category
一、远程索引库
什么是远程索引库?
每创建一个组件都会带有一个 xxx.podspec 的索引文件。专门用来存放这些索引文件的库就叫做索引库。我们需要将这些索引文件上传到远程索引库才能保证其他的同事能够拿来用。
创建远程索引库( 注:这里是在github上创建了一个public的organization名字叫FFComponent)笔者这里创建的public的,自己公司的项目创建private的私有索引库即可,私有索引的步骤和pubic的操作方式一样

远程索引库已经创建成功,可以看到远程索引库的地址

二、 本地索引库 (本地索引库就是用来存放本地索引文件的库)
1. 打开终端 pod repo 查看一下当前有哪些本地索引库(如果你之前没有创建过,应该只有一个master)

2. 通过pod repo add <本地索引库的名字> <远程索引库的地址> ,创建本地索引库并和远程索引库做关联(注:本地索引库的名字建议和远程索引库起的名字一样)

3. 通过下面的方式可以查看本地索引库的物理地址

三、远程代码库 (代码实际存放的远程仓库)
创建远程代码仓库(和创建远程索引库的方式一样),创建一个FFCategoryKit的远程代码库,用来存放FFCategory组件的代码。同样获取到FFCategoryKit组件的远程代码库地址。


四、本地代码库
创建FFCategoryKit组件本地代码库
1. pod lib create <组件名> 创建本地代码组件模版库(根据自身需求对下面的提示信息做选择就好)

2. 编译swift版本错误解决,修改一下ReactiveCocoa和Result的UseLegacySwiftLanguage Version 为YES。

3 .编译运行通过看下效果。接着把FlowerField_Component 里的Others路径下的文件夹拖入到组件FFCategoryKit的classes路径下。

4. 接着cd到Example下进行pod install (把刚才拖入到classes里的文件夹pod进来)

5. 编译组件看是否报错,编译通过后需要修改podspecs索引文件,一般需要修改下面几个问题。
a. 修改版本号
b. 修改项目的简单概述和详细描述
c. 修改homepage和source地址
d. 添加依赖库
修改前的状态如下图所示:

修改对应的地方即可

修改后如下:

6. 编译运行通过后,提交组件到远程代码库并打tag.
- git add .
- git commit -m “xxx"
- git remote add origin 远程代码仓库地址
- git push origin master
- git tag 版本号 (注:这里的版本号必须和podspec里写的版本号一致)
- git push --tags

7. 接下来可以验证podspec索引文件是否正确
首先,通过pod lib lint FFCategoryKit.podspec --verbose --allow-warnings 验证本地索引文件是否正确
也可以略过本地验证
直接通过pod spec lint --verbose --allow-warnings 命令验证podspec索引文件(既验证本地同时验证远程的podspec)
这个命令是验证远程podspec是否正确,就是验证提交到代码仓库里的podspec是否正确
--verbose 是为了打印更加详细的信息方便查看
有时会遇到这样的错误
验证时发现`source_files`匹配不到文件

此时,去远程代码库看看对应的Classes是否为空

错误说明:如果遇到下面的错误,就按照提示在终端输入`echo "2.3" > .swift-version`,然后在继续验证,验证通过后再通过 pod repo push 提交到远程索引仓库,如果没有遇到该问题自动略过.



8. 验证通过后,pod repo push <本地索引库> <索引文件名> - -verbose - -allow-warnings 提交索引文件到远程索引库。

本地也可以查看已成功

9. 接下来回到FlowerField_Component工程修改podfile文件,把FFCategoryKit组件pod进来(注:需要在Podfile中指定组件远程索引库地址,如果不指定默认会从master的索引库查找就会报找不到组件)

Podfile文件修改如下 :

执行pod install,按照上面同样的方式修改一下ReactiveCocoa和Result的UseLegacySwiftLanguage Version 成YES

同样在项目查看已经pod进来了。

10. 编译运行如下

第二步:基础组件APIs
对项目FlowerField_Component中Others下的APIs实施组件化,因为APIs组件中会依赖AFN框架,所以这里会说明一下。按照和FFCategoryKit组件同样的方式来创建APIs组件对应的远程代码库,本地代码模版库,索引库就不用创建了。
1. FFAPIs组件远程代码库和本地代码模版库


2. 同样将APIs文件夹拖入到FFAPIsKit下的classes下面。因为APIs中的NetworkHelper类依赖AFN框架,所以需要修改podspec索引文件,修改如下:

3. 回到Example进行pod install, 编译运行通过后进行提交组件到远程代码库。按照同样的方式打好tag,验证podspec并提交索引文件。然后在本地索引库查看发现FFAPIsKit组件已经完成了。

4. 最后,同样回到FlowerField_Component修改Podfile后pod install。修改ReactiveCocoa和Result的UseLegacySwiftLanguage Version 为YES后编译运行。

5. 采用同样的方式对configs、reformerKeys、tools、mainView进行组件化,创建组件FFConfigsKit、FFReformerKeysKit、FFToolsKit、FFMainViewKit。
说明:FFToolsKit 依赖MBProgressHUD库; 别忘记在对应的podspec索引文件中添加依赖库。

6. 基础组件化结束之后,FlowerField_Component中Podfile如下:

首先感谢您能坚持听我啰嗦到这里。到这里上篇就结束了。后两篇将陆续更新出来。项目组件化过程中一定也会遇到各样的问题。欢迎大家留言交流互相学习。我的邮箱 jiajung@aliyun.com 也欢迎大家Emial交流。
小礼物走一走,来简书关注我
写的不错 不过有个地方是是泛型 不是范型
佳哥牛逼
lz 求教下。如果我组件a依赖了afn 组件b也依赖afn 那我需要同时在组件a的和组件b的spec里配置依赖吗?
我现在在组件a里配置了个afn依赖。发现主工程目录中直接导入了afn框架。。
如果我在组件b里在配置afn依赖。。这样会出问题吗?还是主工程只会导入一次?还是有其它方法相互依赖只需要其中一个组件导入afn就可以了?
楼主很赞,条理清晰,通俗易懂,就是demo没有数据,小菊花转个不停,有时间修复一下啦
FFAPIsKit会依赖FFToolsKit(DBManager, FFHelper),在FFAPIsKit.podspec里添加依赖 s.dependency 'FFToolsKit' , 不行啊,怎么处理FFAPIsKit依赖FFToolsKit的情况呢?
楼主,demo接口挂掉了,一直转菊花
楼主按照你的方法试了很多次了,pod spec lint 的时候在Example目录下总是找不到,来到根目录可以找到,但是添加到项目中pod install 后发现pod里是一个空文件夹,麻烦解决一下
编译报错,'FMDB/FMDatabase.h' file not found,在FFCategoryKit.podspec里已经有加入 # s.dependency 'FMDB',依然是报错,请问这个怎么解决呢
一直在转菊花是什么情况呢、
楼主的kit是swift语言的?
去model话那个方式,假如其中有一个value为空的话,怎么防止项目崩溃呢?
看了完三篇文章,我在想你对组件化是不是有点误解?
podspec索引文件的s.source_files配置我还有点疑问。我在Classes文件夹下不是直接放入类文件,而是放入一个有多个层级的文件夹,这时候s.source_files路径要怎么配置啊?
我有一个问题,如你上面所说,APIs中的NetworkHelper类依赖AFN框架,在pod install之后,编译会不会通不过啊?
楼主你好,我想请教个问题:
在组件化后,每个组件内所需要的网络请求,你是写在组件代码内了,还是怎样实现的呢?
作者:山色归读 侵权必究,授权转载请见文尾。 《水浒传》中地慧星扈三娘,绰号“一丈青”,与母大虫顾大嫂、母夜叉孙二娘合称梁山三女将。她本是扈家庄扈太公的长女、祝家庄祝彪的未婚妻。宋江攻打祝家庄时,因当时的独龙冈上的祝家庄、扈家庄和李家庄这三个最有势力的庄子鼎立结盟,共同进退,再加之扈家庄因与祝家庄的姻亲关系派兵救助,最后她被豹子头林冲所擒。 宋江捉住扈三娘后派人连夜送上梁山,交给其父宋太公看管。等宋江率领得胜之师回到水泊梁山大寨时,他急冲冲地让老爹认扈三娘为义女,自然而然的扈三娘也就成了他的义妹。最后扈三娘被他指婚配给她手下败将矮脚虎王英。 她为什么会成为松江的义妹?说白了,这是松江的...
山色归读茫茫人海中很难寻找到你,只能请你自己来了,留个言,让我们知道你的存在。知道简书中还有这么优秀的作者,而不仅仅只看榜单中的那些人。 能简要做个写作风格的自我介绍就更好了。 福利:选取6人,每人赠送5贝。3月12日中午12时截止 给大家介绍一群热爱写作的人(持续更新中) 自评用户:(有补充介绍的请留言) 爱吃土豆的佩奇 : 没有风格算不算最好的风格。我是佩奇 简书一枚热心小可爱 艾米在渔村 我没有鲜明的风格,只是写一些自己喜欢的文字,自娱自乐。我是艾米在渔村热爱生活,喜欢花花草草 千漫千寻: 又写又画自娱自乐的千漫 米尔_498d 吃瓜喝茶嗑瓜子看大戏是我的最强爱好,偶尔会携一些思索类文章...
小小亮FLY这是朋友讲给我听的真实故事,主角是朋友的朋友。听罢感慨良多,遂以文字记之。谁也不知明天和意外哪个先来,惟愿珍惜每一天,不负眼前人。下面,我用第一人称来叙述。 文/婉兮 图/网络 1 靳晨的死讯,是2月22日上午10点传开的。 当时,我刚刚开完早会,正准备喝一天中的第二杯水,也就顺手打开了朋友圈。然后,一行类似讣告的文字闯到眼前来: “我是靳晨的父亲,靳晨昨夜突发心梗,送医抢救无效,于今早8:51逝世于市第一人民医院。” 无法形容那一瞬间的心情,确切地说,那是短暂的失忆和窒息。直到身边的同事连喊两声,我才从另一个世界回过魂来,把杯子轻轻放下。 杯子是我最爱的天蓝色,靳晨送的。 当时我们还...
婉xi转自:许莫私人音乐厅作品 在爱里面的人最容易犯傻 当一个人喜欢上另一个人的时候 就会忍不住想关心他的一切 与距离的远近无关 与时间的长短无关 哪怕只能在背后默不作声 他的风吹草动 点点滴滴在你这里都是牵挂 在乎一个人就会变得敏感 话说轻了怕他不懂 话说重了 ,怕他受伤 冷淡了 怕他想多 生病了 怕他心疼 因为爱的深,所以忍不住 哪怕分开了 记得TA的联系方式 总是忍不住去看关于TA的任何消息 总是希望对方会给自己发消息 因为爱,所以忍不住想念 有时候谁关心我们 谁忽视我们 其实我们很容易就是识别出来 偏偏在爱里面的人 最容易犯傻 爱一个人的时候 哪怕对方已经不爱...
月宸我年过花甲,姥爷级别,好在简书涂鸦。最近,简村入驻一批美女,老夫寒舍也是门庭若市,每天有不少90后、95后、甚至00后的小美女光顾、来访并关注我,他们的基本特征是:头像美女;不点赞,是否阅读文章不知道,估计也不会看;不写文,0字0喜欢;暂无简介。看她们的动态,这些人关注量很大,既关注作者,也关注作者的某一篇文章。 对这样的一批人,尽然还有好多粉丝,说明我们简友们不明真相,出于礼貌而回了粉。 简书其他好友也有反映这一问题的。那么对突然冒出来的这么多90后0字美女粉丝,她们是真的关注你吗?她们是真人注册的吗?她们注册简书的真实目的又是什么呢……?一连串的问号在我脑海里盘旋不去。 我分析,这些...
沙舟wang
188道iOS中高级核心面试题,我只能帮你在这里了!
在2018年底,小编混迹在各种iOS交流群中,整理出了将近两百道大厂最喜欢在面试问到的问题,今天在这里分享给大家[免费获取方式在最后]! 小编就不在这里全部列举出来了,可以在前面的看到,文档里面包括了目前比较受欢迎的所有面试题!有需要获取的话,可以加QQ群:67988454...
iOS持续集成—Jenkins(最新最全)
搭建Jenkins前,请确认mac系统上已经搭建好了Java环境。从零开始一步一步构建,遇到了很多坑,好在最终success了。 一、 搭建Jenkins 1. 安装Jenkins 从官网上下载pkg安装包 这里以jenkis-2.73.3.pkg包为例 安装完成后,Saf...
iOS大厂面试题总结
1. OC语法 1. OC中对象的结构(腾讯一面) Instance对象如果是NSObject对象,对象中只有一个isa指针,在64位中占16个字节(可以通过malloc_size函数获得),但实际只用到了8个字节(可以通过class_getInstanceSize函数获得...
深入iOS系统底层之映像操作API介绍
绿树阴浓夏日长,楼台倒影入池塘。--《唐高骈·山亭夏日》 mach-o文件和进程的映像(image) iOS系统生成的可执行程序或者动态库文件的存储布局格式被称之为mach-o格式。文件中存放着程序的代码和数据,而程序运行时系统会为其建立一个进程,以及分配虚拟内存空间。同时...
鹅厂,iOS高级开发精选面试题!
前言 为防止背题,大部分题目不设标准答案,重点考察面试者的基础知识和思维逻辑,答案的提示见后面。 正文 题目1、举例两个遇到过印象深刻的外网Crash,并介绍如何发现、定位、解决;题目2、举例两个性能问题的优化,并介绍如何发现、定位、解决以及原理;题目3、介绍Objecti...
听一位妈妈抱怨:儿子原本很听话,但读初二以后,突然像变了一个人似的,父母跟他说什么话,他都要顶撞回来,有时干脆不搭理。期中考试成绩有进步,妈妈本想趁这个机会跟孩子好好沟通一下,没想到孩子反应让人无语: 妈: 儿子你真棒,这次成绩不错,妈为你骄傲!晚上想吃啥,妈妈给你做? 子...
引入不蒜子 这段代码可以写在footer.ejs里或者header.ejs里或者layout.ejs里 添加站点访问量 通常站点的总访问量会显示在footer的位置,所以我们可以在footer.ejs里加上如下标签: 计算访问量的方法有两种:算法a:pv的方式,单个用户连续...
1.Android Studio遇到的问题AS,只要修改build.gradle配置文件,整个AS就卡顿ps:AS Version: Gradle Plugin: com.android.tools.build:gradle:1.3.1Gradle: 2.5有遇到相同问题的...
T










浙公网安备 33010602011771号