iOS组件化(上篇)- 拆分基础组件

修改说明:有朋友反映Xcode9编译RAC4.x失败,无法设置Use Legacy Swift Language Version 的问题。现在将RAC版本改成了2.5(OC版本)。感谢朋友们的反馈。git clone 之后执行pod install即可。不用再设置UseLegacySwiftLanguage Version。

      组件化在业界已经炒的水深火热,关于组件化的好处和组件化的方案网上已经有大篇的文章了。(Casa的这篇文章对几种方案的利弊讲述的很不错)笔者就不在赘述了。笔者通过拆分一个现有的demo来简单聊一下项目实施组件化的过程(将分为上篇、中篇、下三篇)。

iOS组件化(上篇)- 拆分基础组件

iOS组件化(中篇)-拆分业务组件

iOS组件化(下篇)-加载XIB、图片资源



## 一、Demo简要说明


demo可以从github下载(下载之后执行pod install ), 因为之前用RAC+MVVM写项目,这里用了RAC4.x。pod install之后需要修改一下ReactiveCocoaResult的UseLegacySwiftLanguage Version 为YES。按照下面的方式解决即可:

修改Result
修改ReactiveCocoa

设置好之后编译运行就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的操作方式一样

FFSpecs索引库

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

索引库地址

二、 本地索引库 (本地索引库就是用来存放本地索引文件的库)

1. 打开终端 pod repo 查看一下当前有哪些本地索引库(如果你之前没有创建过,应该只有一个master)

pod repo查看本地索引库

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

命令

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

物理地址

三、远程代码库 (代码实际存放的远程仓库)

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

FFCategory组件
远程代码库地址

四、本地代码库

创建FFCategoryKit组件本地代码库

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

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

UseLegacySwiftLanguageVersion 为YES

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

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

5. 编译组件看是否报错,编译通过后需要修改podspecs索引文件,一般需要修改下面几个问题。

a. 修改版本号

b. 修改项目的简单概述和详细描述

c. 修改homepage和source地址

d. 添加依赖库

修改前的状态如下图所示:

修改前的状态

修改对应的地方即可

修改后如下:

podspec修改后

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是否为空


图片发自简书App



错误说明:如果遇到下面的错误,就按照提示在终端输入`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组件远程代码库和本地代码模版库

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如下:

FlowerField_Component的Podfile

首先感谢您能坚持听我啰嗦到这里。到这里上篇就结束了。后两篇将陆续更新出来。项目组件化过程中一定也会遇到各样的问题。欢迎大家留言交流互相学习。我的邮箱 jiajung@aliyun.com  也欢迎大家Emial交流。

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

推荐阅读更多精彩内容