参考资料:

http://wereadteam.github.io/2016/03/19/iOS-Component/#more

https://casatwy.com/iOS-Modulization.html

https://casatwy.com/modulization_in_action.html#

https://www.jianshu.com/p/b1c6d070c92b

这篇文章只是一个学习过程的记录,因为我发现相关文章都没有比较详细的操作记录,而这个过程必定不可能像他们博文中写的那样顺利,因此这篇文章主要是两点,一点是关于组件化的主要过程和结构梳理,另外就是使用pod过程中会遇到的各种问题.至于对组件化的使用,我直接引用其中一篇文章的总结:"实际上我没有组件化相关的实践,这里仅从 limboy 和 casa 提供的这几个方案对比分析,我还对组件化带来的收益是否大于组件化增加的成本这点存疑,相信真正实践起来还会碰到很多坑,继续探索中。"

这篇使用方案一,直接把这句话拿过来就是"总结起来就是,组件通过中间件通信,中间件通过 runtime 接口解耦,通过 target-action 简化写法,通过 category 感官上分离组件接口代码".

1.总流程

1.新建主项目仓库,也就是需要组件化的项目,并将主项目push到远端仓库.

2.新建组件A项目及远端私有仓库,并同步.

3.新建组件A与外界联系媒介A_Category项目及远端仓库,并同步.

4.新建组件B项目及远端仓库,并同步.

5.新建组件B与外界联系媒介B_Category项目及远端仓库,并同步.

6.主项目Podfile本地引用组件A和组件B,并使项目编译通过.

7.将本地引用改为远程引用,运行项目并编译成功,组件化完成.

2.开始操作

1.新建主项目仓库,也就是需要组件化的项目,并将主项目push到远端仓库

直接把这个项目(https://github.com/ModulizationDemo/MainProject)拿过来用就可以了,自己写也可以,很简单的push两个页面,需要先pod install,因为使用了作者的一个简单的布局库.

运行无误之后就可以push到自己的主项目仓库了.

在码云新建仓库MainProject,clone到本地,把刚才的项目拖进去,然后push到远程仓库.

2.新建组件A项目及远端私有仓库,并同步.

先在码云新建仓库A_Section,然后clone到本地,然后在本地目录新建Xcode项目,也就是组件A项目,这里直接使用pod命令:

pod lib create A_Section

按提示输入选项,项目便创建好了,把项目文件中git相关的文件删掉,然后放到刚才clone到本地的路径,.

(用这条命令直接生成模板在模板中编辑不容易出错,如果自己用Xcode创建新项目,用

pod spec create A_Section  https://gitee.com/xxxxxxx/A_Section.git

这条命令去创建.podspec文件,再去编辑的话,很容易遇到一些奇奇怪怪的错误,比如死活报错找不到source_files,而花费时间解决这些奇怪的麻烦并不明智.)

接下来可以删掉本地引用库里面的ReplaceMe.m文件,不删也不会报错.然后在项目中新建Classes文件夹

接下来就可以把MainProject中属于A组件的代码移动到这里了.

也就是

运行项目,编译报错,可以看到AViewController中使用了HandyFrame库,因此需要在Podfile中加入HandyFrame:

第一行是引用本地库,也就是ReplaceMe.m所在路径,不需要管,注释掉,以免产生不必要的影响,再次执行

pod install

  重新打开项目,运行可以发现仍然报错,因为AViewController里现在直接引用了BViewController:

而组件化的目的就是消除组件之间的直接引用,所以删掉BViewController头文件引用,并将代码中的BViewController代码注释掉消除报错.

把主界面设置成AViewController

再次运行成功,并显示AViewController界面.

接下来编辑A_Section.podspec文件

接下来添加Target_A:

代码如下:

//  Target_A.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Target_A : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end NS_ASSUME_NONNULL_END // Target_A.m #import "Target_A.h"
#import "AViewController.h" @implementation Target_A
- (UIViewController *)Action_viewController:(NSDictionary *)params {
AViewController *viewController = [[AViewController alloc] init];
return viewController;
} @end

至此执行下一步,push到远端仓库后面再做,因为代码还要改动.

3.新建组件A与外界联系媒介A_Category项目及远端仓库,并同步.

同上,先在码云新建一个A_Category私有仓库,然后clone到本地,然后在本地目录新建Xcode项目,直接使用pod命令:

pod lib create A_Category

按提示输入选项,项目便创建好了,把项目文件中git相关的文件删掉,然后放到刚才clone到本地的路径.

打开项目Podfile,添加

pod 'CTMediator'

  注释掉本地引用,执行

pod install

  新建分类

编写代码:

//  CTMediator+A.h

#import <CTMediator/CTMediator.h>

@interface CTMediator (A)
- (UIViewController *)A_aViewController;
@end // CTMediator+A.m #import "CTMediator+A.h" @implementation CTMediator (A)
- (UIViewController *)A_aViewController {
return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];
}
@end

至此执行下一步,同样push到远端仓库留到后面验证过代码没有问题再和其他组件一起做.

别忘了编辑A_Category.podspec文件,以及添加依赖

s.dependency 'CTMediator'

参照前面.

4.新建组件B项目及远端仓库,并同步.(参考2)

先在码云新建仓库B_Section,然后clone到本地,然后在本地目录新建Xcode项目,也就是组件B项目,这里直接使用pod命令:

pod lib create B_Section

按提示输入选项,项目便创建好了,把项目文件中git相关的文件删掉,然后放到刚才clone到本地的路径,.

在项目中新建Classes文件夹

接下来就可以把MainProject中属于B组件的代码移动到这里了.

也就是

运行项目,编译报错,可以看到BViewController中使用了HandyFrame库,因此需要在Podfile中加入HandyFrame:

第一行是引用本地库,也就是ReplaceMe.m所在路径,不需要管,注释掉,以免产生不必要的影响,再次执行

pod install

  重新打开项目,运行成功:

把主界面设置成BViewController

再次运行成功,并显示BViewController界面.

接下来编辑B_Section.podspec文件

接下来添加Target_B:

代码如下:

//  Target_B.h

#import <Foundation/Foundation.h>

@interface Target_B : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end // Target_B.m #import "Target_B.h"
#import "BViewController.h" @implementation Target_B
- (UIViewController *)Action_viewController:(NSDictionary *)params {
NSString *contentText = params[@"contentText"];
BViewController *viewController = [[BViewController alloc] initWithContentText:contentText];
return viewController;
}
@end

至此执行下一步,push到远端仓库后面再做,因为代码还要改动.

5.新建组件B与外界联系媒介B_Category项目及远端仓库,并同步.(参考3)

先在码云新建一个B_Category私有仓库,然后clone到本地,然后在本地目录新建Xcode项目,直接使用pod命令:

pod lib create B_Category

按提示输入选项,项目便创建好了,把项目文件中git相关的文件删掉,然后放到刚才clone到本地的路径.

打开项目Podfile,添加

pod 'CTMediator'

  注释掉本地引用,执行

pod install

  新建分类

编写代码:

//  CTMediator+B.h

#import <CTMediator/CTMediator.h>

@interface CTMediator (B)
- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText;
@end // CTMediator+B.m #import "CTMediator+B.h" @implementation CTMediator (B)
- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText {
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"contentText"] = contentText;
return [self performTarget:@"B" action:@"viewController" params:params shouldCacheTarget:NO];
}
@end

至此执行下一步,同样push到远端仓库留到后面验证过代码没有问题再和其他组件一起做.

别忘了编辑B_Category.podspec文件,以及添加依赖

s.dependency 'CTMediator'

参照前面.

6.主项目Podfile本地引用组件A和组件B,并使项目编译通过.

把前面的四个项目文件copy到MainProject文件夹,并编辑MainProject项目的Podfile文件

由于A_Section和B_Section中都依赖了HandyFrame,所以会自动pod 'HandyFrame',因此这里不写pod 'HandyFrame'也可以.

执行

pod install

运行MainProject,成功.

此时在ViewController中还是直接引用的AViewController,组件化之后调用AViewController应该使用A_Category.

因此在ViewController.m文件中做如下修改:

#import "ViewController.h"
#import <HandyFrame/UIView+LayoutMethods.h>
#import "CTMediator+A.h" ... #pragma mark - event response
- (void)didTappedPushAViewControllerButton:(UIButton *)button
{
UIViewController *viewController = [[CTMediator sharedInstance] A_aViewController];
[self.navigationController pushViewController:viewController animated:YES];
}

 编译通过之后,进行下一步,现在已经可以从ViewController访问AViewController,接下来就是从AViewController访问BViewController,所以接下来编辑AViewController.m文件:

#import "AViewController.h"
#import <HandyFrame/UIView+LayoutMethods.h>
#import "CTMediator+B.h" ... #pragma mark - event response
- (void)didTappedPushBViewControllerButton:(UIButton *)button
{
UIViewController *viewController = [[CTMediator sharedInstance] B_viewControllerWithContentText:@"夏帆帆"];
[self.navigationController pushViewController:viewController animated:YES];
}

  再次运行成功,至此本地引用测试通过.接下来就是改成远程私有库引用了.

7.将本地引用改为远程引用,运行项目并编译成功,组件化完成.

将B_Category运行编译通过之后,push到远端私有库,并打上tag:

git tag 0.0.1
git push --tags

本地验证.podspec文件

pod lib lint --allow-warnings

 远程验证.podspec文件

pod spec lint --allow-warnings

 将B_Category.podspec文件提交到之前添加的本地私有索引库PrivatePodRepo

pod repo push PrivatePodRepo B_Category.podspec --verbose --allow-warnings 

  远程验证和提交的时候很容易出现这个错误,比如:The `source_files` pattern did not match any file.

但是检查发现source_files路径是没问题的,出现这种情况的时候,先查看一下远端仓库和本地代码是否同步,tag是否相同,如果一切都没有问题却还是报这个错误,

去这个文件夹

/Users/Library/Caches/CocoaPods/Pods/External/项目文件

  把 s.source_files = 'Example/B_Category/Classes/**/*',对应的Example文件夹拷贝到项目文件位置,再次提交即可.

这个问题我觉得是工具的锅,报错信息莫名其妙,而且我也没有发现确切的流程上有什么明显问题,因为不是我每次做都会出现这个错误,所以我只找到了解决这个问题的办法,但是还不知道如何完全避免,如果有谁知道怎样避免这个问题,欢迎不吝赐教.

同样我认为它不管什么问题都报一样的错误,有很大的误导性,这是软件设计的问题,因此我觉得并没必要花太多时间去纠结这些使用工具上细枝末节的问题,解决使用上的问题即可.

作为程序员,做出来的软件使用上麻烦容易出错,不用多考虑一定是开发者的有问题,而不是使用者的问题.(pod添加私有库的过程相当容易出错,尤其是最后push到仓库,很可能修改多次,push多次才能成功,因为只要有一点本地修改,以及版本号的修改,和远端仓库不一致,就不能添加成功,就要再push,再打tag,然后再尝试再发现问题再修改,循环这个过程直到.podspec成功添加到PrivatePodRepo仓库为止)

下面A_Category和B_Category一样,都是这个流程,就不多说了.

接下来将第6步中的AViewController和BViewController修改同步到copy之前的项目中,会发现A_Section编译报错,是因为它引用B_Category了但是还没有pod 'B_Category',将B_Category项目copy到A_Section,在Podfile文件中本地引用.

pod install

运行通过.

然后将本地引用改为远端引用:

pod install

运行成功,别忘了到.podspec文件中添加B_Category依赖,

但是到了这一步之后,验证就出现各种问题,

    - ERROR | [iOS] unknown: Encountered an unknown error (Unable to find a specification for `B_Category` depended upon by `A_Section`

  先是pod lib lint报以上错误,更改命令为:

Pod lib lint  A_Section.podspec --sources=https://gitee.com/alan12138/PrivatePodRepo.git,https://github.com/CocoaPods/Specs.git --allow-warnings

  将私有源添加到命令,依然会报错

 - ERROR | [iOS] xcodebuild: Returned an unsuccessful exit code. You can use `--verbose` for more information.
- NOTE | xcodebuild: note: Using new build system
- NOTE | [iOS] xcodebuild: note: Planning build
- NOTE | [iOS] xcodebuild: note: Constructing build description
- NOTE | xcodebuild: /Users/zhangwei/Desktop/A_section/A_Section/Example/A_Section/Classes/AViewController/AViewController.m:11:9: fatal error: 'CTMediator+B.h' file not found

  竟然说找不到B_Category库的头文件,这个问题我没找到合适的解决办法,网上相关的资料也很少,而且这些组件化过程中pod遇到的各种问题,在所有相关文章中都基本没有提到,我不知道他们是不是真的用的那么顺利.cocoaPods 的github有这个问题的反馈,但是并没有解决,让到Stack Overflow去问,一样没有解决办法,浪费了很多时间之后,这里我准备放弃,前面我说过了,没必要花太多时间去解决工具的糟糕设计上产生的问题,因此我将这里B_Category直接放到本地.如果有人知道解决办法,欢迎不吝赐教.

综上也就是,在一个私有库中引用另外一个私有库,不论是本地引用还是远程引用,都会出现这个莫名其妙的  file not found ,因此我直接将B_Category导入A_Section项目使用,规避这个问题.

然后

pod install
pod lib lint
pod repo push PrivatePodRepo A_Section.podspec --verbose --allow-warnings

  最后B_Section,参考上面流程,不再多说.

接下来回到MainProject,将本地pod改为远程pod:

pod install之后发现,

没有代码文件被下载下来,因此一定是目录出了问题,查看Git仓库目录发现,索引库中的.podspec文件中的s.source_files是从每个Pod的Git仓库目录中去找的,而在我的A_Category库中,最外层不是Example,而是A_Category:

因此修改A_Category.podspec文件的s.source_files:

再次提交A_Category.podspec索引,然后MainProject移除pod 'A_Category',pod install,再添加pod 'A_Category',再pod install,即可.

其他都同样修改.

最后pod install,运行成功,组件化完成.

因为pod仓库之间文件是互相不可见的,因此这里并不会出问题.这个样子也算是对pod奇葩问题最后妥协的结果.当然这样做是有很大问题的,所以你可以选择到第六步为止,摆脱了pod的折磨,也并不影响组件化.

当然也是为了尽快脱坑这篇文章,因为在这篇文章里为了解决各种各样的pod校验问题浪费了大量时间,而个人认为在这种工具软件的使用问题上浪费大量时间是非常不划算的一件事,除非你非它不可,或者是它的开发者. 而我尽量去列出遇到过的所有问题并给出找到的解决方法,遗憾的是最终有一个问题没有找到答案,我依然觉得这是cocoaPods本身的问题,使用过程中容易出问题的地方太多太多.

所以,与其浪费大量时间跟cocoapods较劲,不如做些更有意义的事,毕竟我们需要的是组件化的思想和实践,而不是pod的使用大全,而这些到第六步已经完成了.



iOS组件化实践的更多相关文章

  1. iOS 组件化

    iOS 组件化介绍 随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间 ...

  2. 使用CocoaPods创建自己的私有库-iOS组件化第一步

    目前iOS组件化常用的解决方案是Pod+路由+持续集成,通常架构设计完成后第一步就是将原来工程里的模块按照架构图分解为一个个独立的pod工程(组件),今天我们就来看看如何创建一个Pod私有库. 新建: ...

  3. iOS 组件化路由框架 WisdomRouterKit 的应用

    [前言] 大家好,写作是为了和读者沟通交流,欢迎各位开发者一起了解 WisdomRouterKit SDK 的功能. 关于 iOS 组件化路由方案框架: WisdomRouterKit 的功能介绍,之 ...

  4. iOS组件化实现方案

    作者原文iOS组件化 - 路由架构从0到1实战  合伙呀 1.CTMediator作为路由中间件 2.基础UI组件以pod形式引入,并且能够独立运行调试 3.基础工具组件以pod形式引入,并且能够独立 ...

  5. iOS 组件化的几篇文章

    随着工程的成长,开发人员的增多,合理的模块划分及低耦合的重要性显得愈发重要.最近在思考这方面的问题,也读了不少通过组件化解耦的文章,这里记录一下. 前 5 篇文章有些关联,建议阅读顺序,1.3.2.4 ...

  6. Category 特性在 iOS 组件化中的应用与管控

    背景 iOS Category功能简介 Category 是 Objective-C 2.0之后添加的语言特性. Category 就是对装饰模式的一种具体实现.它的主要作用是在不改变原有类的前提下, ...

  7. iOS组件化思路 <转>

    随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间的代码耦合,为了更好 ...

  8. iOS组件化思路-大神博客研读和思考

    一.大神博客研读 随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间的 ...

  9. iOS 组件化 —— 路由设计思路分析

    原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...

随机推荐

  1. HBase MapReduce 一些 ClassNotFoundException 所缺少的jar包

    我们在用 java 操作 HBase  时,可能会出现相关的 ClassNotFoundException  等异常信息,但是我们又不想把 HBase lib 下的所有jar包全部导入到工程,因为会有 ...

  2. 高性能MySQL之事物

    一.概念 事务到底是什么东西呢?想必大家学习的时候也是对事务的概念很模糊的.接下来通过一个经典例子讲解事务. 银行在两个账户之间转账,从A账户转入B账户1000元,系统先减少A账户的1000元,然后再 ...

  3. android ——网络编程

    一.WebView 这个View就是一个浏览器,用于展示网页的. 布局文件: <LinearLayout xmlns:android="http://schemas.android.c ...

  4. intellIJ IDEA学习笔记2

    常用的有fori/sout/psvm+Tab即可生成循环.System.out.main方法等boilerplate样板代码 例如要输入for(User user : users)只需输入user.f ...

  5. [Spring cloud 一步步实现广告系统] 18. 查询返回广告创意

    根据三个维度继续过滤 在上一节中我们实现了根据流量信息过滤的代码,但是我们的条件有可能是多条件一起传给我们的检索服务的,本节我们继续实现根据推广单元的三个维度条件的过滤. 在SearchImpl类中添 ...

  6. 前端插件之Datatables使用--下篇

    工欲善其事,必先利其器 本系列文章介绍我在运维系统开发过程中用到的那些顺手的前端插件,上一篇文章介绍了Datatables插件的基本使用,这一篇文章作为上一篇的延续,会介绍Databases的一些高级 ...

  7. mybatis 源码分析(一)框架结构概览

    本篇博客将主要对 mybatis 整体介绍,包括 mybatis 的项目结构,执行的主要流程,初始化流程,API 等各模块进行简单的串联,让你能够对 mybatis 有一个整体的把握.另外在 myba ...

  8. 安装Windows Server 2008

    下面介绍一下,Windows Server操作系统安装,以及在企业中的应用,在小型企业中可以使用不同的版本搭建不同类型的服务,小型企业中可以搭建Web服务,FTP服务,以及DNS和DHCP服务等,在大 ...

  9. Java进程故障排查

    故障分析 # 导致系统不可用情况(频率较大): 1)代码中某个位置读取数据量较大,导致系统内存耗尽,进而出现Full GC次数过多,系统缓慢: 2)代码中有比较消耗CPU的操作,导致CPU过高,系统运 ...

  10. **p,*p和&p使用有感

    *p两种使用情况: 1.定义指针变量,如char *p:这里p是一个变量,单单在这一点上与int a 无差别:但p这个变量特殊在其中只能存地址. 引申:对于char **p,p中存一个地址add1,a ...