iOS中关于模块化开发解决方案(纯干货)

关于iOS模块化开发解决方案网上也有一些介绍,但真正落实在在具体的实例却很少看到,计划编写系统文章来介绍关于我对模块化解决方案的理解,里面会有包含到一些关于解耦、路由、封装、私有Pod管理等内容;并编写的一个实例项目放在git进行开源[jiaModuleDemo],里面现在已经放着一些封装的功能模块;会不断的进行更新,假如你感兴趣可以Star一下,项目也不断的更新完善优化;如果你有更好的方案或者说好的建议可以lssues,我会在短时间进行更新并修改相应的问题;

一:项目中存在的问题

1:当公司里面有多个项目同时进行,并且有可能是多个人分别不同项目时,就会存在如上图出现的情况,其实每个APP中都是有很多共同的模块,当然有可能你会把相同功能模块代码复制一份在新项目中,但这其实并不是最好的方式,在后期不断迭代过程中,不同的人会往里面增加很多带有个人色彩的代码;这样就像相同的模块项目后期对于多个项目统一管理也是灾难性,有可能会失控,哪怕项目转移别人接手也会无形中浪费很多时间,增加维护成本,所以实例中更注重对于一些相同模块进行提取,求同存异;而模块化结合私有Pods进行管理,对于常用功能的封装,只要开放出一些简单开关配置方式,就可以实现一个功能,比如日志记录、网络请求模块、网络状态变化提示等;

2:对于页面之间相互耦合,而页面之间的传参也各不相同,由于不同的开发人员或者简便方式等原因,传参的类型都有差异,包含如实体、简单基本类型等,先前项目对于路由方式也不支持,导致要实现收到消息推送进行不同的页面跳转存在硬编码情况,对于功能扩展存在相当大的问题;而右边则是模块化后页面之间的交互方式;页面之间也不存在耦合关系,都只跟JiaMediator这个中介者相依赖;而传参都统一成以字典的形式;虽然可能牺牲一些方便跟随意,却可以解耦模块化;并且加入对路由方式的处理;约定好相关的协议进行交互;用这种路由方式代替那些第三方的路由插件则是因为它的灵活性,最主要还是省去了第三方路由插件在启动时要注册路由的问题;

二:解决方案实现之模块化

1:JiaCore(基础功能封装)

JiaCore是整个APP最基础模块,所有的模块化都要依赖,主要包含一些全局的功能模块,比如JiaBaseViewController、JiaAppDelegate等;目前已经把一些默认的功能进行集成在里面,包含网络状态变化判断及提示、日志记录功能等;并把一些相关配置的内容用JiaCoreConfigManager这个管理类进行统一设置,比如是否打开日志记录功能;JiaCoreConfigManager类则是开放给具体APP设置全局的相关配置;下面就以其中一个日志记录功能进行讲解:

//JiaCore基础模块相关配置
JiaCoreConfigManager *jiaCoreConfig=[JiaCoreConfigManager sharedInstance];
jiaCoreConfig.recordlogger=YES;
然后具体APP的PrefixHeader.pch引入命名空间并进行设置记录日志的等级:
#import "JiaCocoaLumberjack.h"
//DDLog等级
static const int ddLogLevel = DDLogLevelVerbose;

这样就完成的一个APP对于日志记录模块的引入,JiaCore已经帮你完成的关于日志记录的相关配置,并且错误内容以一种可读性较好的格式记录到file文件中,而且这些file文件生成规则也都定义好了,当然如何时你要是在Xcode控制台显示不同等级色彩,只要安装XcodeColors插件并简单进行设置就可以了,对于不同等级不同色彩都已经在JiaCore配置完成;

2:JSPatch热更新功能

在JiaCore里面也默认集成了热更新的功能,只要传入简单的对象数组就会启动热更新;其中JiaPathchModel已经是定义好的模型,在APP中把接口请求转化成模型数组,其中patchId是唯一值名称、md5则是JS文件的MD5值、url是JS的下载路径、ver则是对哪个版本起作用;因为一般我们在外面的APP都是多版本共存,热更新也要进行版本区分,只下载与本版本相对应的热更新JS文件加载;而MD5值则是为了增加安全性,避免JS文件被别人进行修改而影响APP的运行,在JiaCore会对下载后的JS文件进行MD5计算并比较;对于没有在jSPatchMutableArray以前的JS文件会被删除;

//热更新内容
JiaPathchModel *sample=[[JiaPathchModel alloc]init];
sample.patchId = @"patchId_sample1";
sample.md5 = @"2cf1c6f6c5632dc21224bf42c698706b";
sample.url = @"http://test.qshmall.net:9090/demo1.js";
sample.ver = @"1";
JiaPathchModel *sample1=[[JiaPathchModel alloc]init];
sample1.patchId = @"patchId_sample2";
sample1.md5 = @"e8a4eaeadce5a4598fb9a868e09c75fd";
sample1.url = @"http://test.qshmall.net:9090/demo2.js";
sample1.ver = @"1";
//JiaCore基础模块相关配置
JiaCoreConfigManager *jiaCoreConfig=[JiaCoreConfigManager sharedInstance];
jiaCoreConfig.jSPatchMutableArray=[@[sample,sample1] mutableCopy];

3:JiaGT模块(个推封装)

消息推送对于一个APP是相当重要性,一般是采用第三方的SDK进行集成,其实大部分的SDK处理代码都是差不多,在这实例中对差异化的内容进行提取,实例中将以个推进行模块化,因为消息推送的大部分代码都集中在AppDelegate中,造成的一大堆杂乱代码,当然也有一部分人对AppDelegate进行扩展分类进行移除代码,实例中将采用另外一种解决方案进行抽取,可以达到完全解耦,在具体的APP里面将不会再出现个推SDK相关内容,只要简单进行配置跟处理消息就可以,下面只是简单的列出部分代码,其它封装代码见源代码;

//设置个推模块的配置
jiaGTConfigManager *gtConfig=[jiaGTConfigManager sharedInstance];
gtConfig.jiaGTAppId=@"0uuwznWonIANoK07JeRWgAs";
gtConfig.jiaGTAppKey=@"26LeO4stbrA7TeyMUJdXlx3";
gtConfig.jiaGTAppSecret=@"2282vl0IwZd9KL3ZpDyoUL7";

#pragma mark 消息推送相关处理
/**
* @author wujunyang, 16-07-07 16:07:25
*
* @brief 处理个推消息
*
* @param NotificationMessage
*/
-(void)gtNotification:(NSDictionary *)NotificationMessage
{
NSLog(@"%@",NotificationMessage[@"payload"]);
NSLog(@"-----接收到个推通知------");
}
/**
* @author wujunyang, 16-07-07 16:07:40
*
* @brief 处理远程苹果通知
*
* @param RemoteNotificationMessage
*/
-(void)receiveRemoteNotification:(NSDictionary *)RemoteNotificationMessage
{
NSLog(@"%@",RemoteNotificationMessage[@"message"]);
NSLog(@"-----接收到苹果通知------");
}
/**
* @author wujunyang, 16-09-21 14:09:33
*
* @brief 获得注册成功时的deviceToken 可以在里面做一些绑定操作
*
* @param deviceToken <#deviceToken description#>
*/
-(void)receiveDeviceToken:(NSString *)deviceToken
{
NSLog(@"-----当前deviceToken:%@------",deviceToken);
}

4:JiaAnalytics模块(友盟统计封装)

JiaAnalytics模块是在友盟统计SDK跟Aspect相结合基础上完成,对于页面的进出统计采用Aop切面方式进行,把原本应该在每个页面生命周期的统计代码移除,App运用只要简单配置友盟相对应的信息,也可以设置要统计页面的过滤条件,目前已经有三种如要统计的开头页面的前缀字符串数组、要统计的页面名称字符串数组、不统计的页面名称字符串数组;可以结合使用,达到精确统计页面的目的;而且把统计的代码放在异步线程进行,不会影响主线程的响应;

__weak typeof(self) ws = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated){
UIViewController *controller = [info instance];
BOOL filterResult=[ws fileterWithControllerName:NSStringFromClass([controller class])];
if (filterResult) {
[ws beginLogPageView:[controller class]];
}
} error:NULL];
[UIViewController aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated){
UIViewController *controller = [info instance];
BOOL filterResult=[ws fileterWithControllerName:NSStringFromClass([controller class])];
if (filterResult) {
[ws endLogPageView:[controller class]];
}
} error:NULL];
});

三:解决方案实现之页面解耦

JiaMediator起到一个中介的作用,所有的模块间响应交互都是通过它进行,每个模块都会对它进行扩展分类(例如:JiaMediator+模块A),分类主要是为了用于本地间调用而又不想用路由的方式,若要用路由的方式则要注意关于路由约束准确编写,它将会直接影响到能否正确响应到目标;实例中也有关于使用通知的方式进行回调参数的回传问题;

实例代码如下:

NSDictionary *curParams=@{kDesignerModuleActionsDictionaryKeyName:@"wujunyang",kDesignerModuleActionsDictionaryKeyID:@"1001",kDesignerModuleActionsDictionaryKeyImage:@"designerImage"};
switch (indexPath.row) {
case 0:
{
UIViewController *viewController=[[JiaMediator sharedInstance]JiaMediator_Designer_viewControllerForDetail:curParams];
[self presentViewController:viewController animated:YES completion:nil];
break;
}
case 1:
{
UIViewController *viewController=[[JiaMediator sharedInstance]JiaMediator_Designer_viewControllerForDetail:curParams];
[self.navigationController pushViewController:viewController animated:YES];
break;
}
case 2:
{
NSString *curRoue=@"jiaScheme://Designer/nativeFetchDetailViewController?name=wujunyang&ID=1001&image=designerImage";
UIViewController *viewController=[[JiaMediator sharedInstance]performActionWithUrl:[NSURL URLWithString:curRoue] completion:^(NSDictionary *info) {
}];
[self.navigationController pushViewController:viewController animated:YES];
break;
}
case 3:
{
NSDictionary *userParaDictionary=@{kUserModuleActionsDictionaryKeyID:@"1"};
UIViewController *viewController=[[JiaMediator sharedInstance] JiaMediator_User_viewControllerForDetail:userParaDictionary];
[self.navigationController pushViewController:viewController animated:YES];
break;
}
default:
break;
}

四:模块化结合私有Pods方案

实例中只是把相关模块化的提取都在一个工程进行体现,最后还是要落实结合Pods进行管理,把每个模块分开管理,不同的APP可以简单通过Pods指令就可以达到引入模块的效果,对于一些相同模块可以在不同的APP重复引用,减小重复开发成本;

以上所述是小编给大家介绍的iOS中关于模块化开发解决方案(纯干货),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Xcode8、iOS10升级问题记录

    1.webView的代理方法: 升级前: - (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error 升级后: - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 要删除NSError前面的 nullable,否则报错. 2.关于触屏事件的一些操作: 升级前: - (void)touchesC

  • iOS Xcode8更新后输出log日志关闭的方法

    刚把Xcode更新到最新的8,一运行发现好多log输出,根据如下操作可以关掉这些log日志,点击项目Edit Scheme - Run - Arguments - Environment Variables里添加:Name:OS_ACTIVITY_MODE  Value:disable 如图: 以上所述是小编给大家介绍的iOS Xcode8更新后输出log日志关闭的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的.在此也非常感谢大家对我们网站的支持!

  • iOS开发之枚举用法小结

    前言 Enum,也就是枚举,从C语言开始就有了,C++.Java.Objective-C.Swift这些语言,当然都有对应的枚举类型,功能可能有多有少,但是最核心的还是一个-规范的定义代码中的状态.选项等"常量". 举个例子,我们想要在开发中根据四季的变化做不同的操作,我们首先想到的可能会是定义一个int型变量,通过为该变量赋不同的值来表示四季(e.g. 1为春天,2为夏天,3为秋天,4为冬天).但是,对于阅读该份代码的人来讲,他们并不能在短时间内就明白数字1234所代表的具体含义.这

  • 更新了Xcode8 及 iOS10遇到的问题小结

    更新了Xcode8 以及 iOS10,App访问用户的相机.相册.麦克风.通讯录的权限都需要重新进行相关的配置,不然在Xcode8中打开编译的话会直接crash. 需要在info.plist中添加App需要的一些设备权限. 相机NSCameraUsageDescription 相册NSPhotoLibraryUsageDescription 通讯录NSContactsUsageDescription 始终访问位置NSLocationAlwaysUsageDescription 位置NSLocat

  • iOS中Xcode 8 日志输出乱码问题的解决方法

    更新到Xcode 8的同学应该都遇到了这个问题:用Xcode 8运行项目,日志会疯狂的刷,就像下面这种图一样: 日志输出 于是,简单搜寻了下,"歪果仁"给出了如下解决方法: Edit Scheme-> Run -> Arguments, 在Environment Variables里边添加 OS_ACTIVITY_MODE = disable 以上所述是小编给大家介绍的iOS中Xcode 8 日志输出乱码问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复

  • iOS为UIView设置阴影效果

    UIView的阴影设置主要通过UIView的layer的相关属性来设置 阴影的颜色 imgView.layer.shadowColor = [UIColor blackColor].CGColor; 阴影的透明度 imgView.layer.shadowOpacity = 0.8f; 阴影的圆角 imgView.layer.shadowRadius = 4.f; 阴影偏移量 imgView.layer.shadowOffset = CGSizeMake(4,4); imgView.layer.s

  • 110.iOS10新特性适配教程XCode8新特性解析

    iOS10 新特性 SiriKit SiriKit的功能非常强大,支持音频.视频.消息发送接收.搜索照片.预订行程.管理锻炼等等.在用到此服务时,siri会发送Intent对象,里面包括用户的请求和各种数据,可以对这个intent处理选择适当的响应. 这个功能主要是看这两个头文件(#import Proactive Suggestions 系统预先建议 背景就是iOS9的时候系统给予的主动建议会通过:Spolight搜索,Safari搜索,Handoff,或者siri建议. 在iOS10之后新增

  • iOS图片模糊效果的实现方法

    本文为大家分享了iOS图片模糊效果的三种实现方式,供大家参考,具体内容如下 1.实现效果依次如图:原图.iOS8效果.Core Image效果. VImage 效果 - 2. 代码 #import "ViewController.h" #import <Accelerate/Accelerate.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {

  • iOS 10 使用相机相簿闪退的bug修正方法

    iOS 10 新规定,在取用相机,相簿,联络资讯,麦克风需要在 Info.plist 加入指定的 key,否则闪退: Info.plist <key>NSPhotoLibraryUsageDescription</key> <string>使用相簿需要您的同意</string> <key>NSCameraUsageDescription</key> <string>使用相机需要您的同意</string> <

  • iOS中关于信鸽推送的使用demo详解

    最近在看推送方面的知识,用的是信鸽推送主要是因为后台用的是信鸽 推送用第三方推送,也就是在客户端建一个广播接收器,当服务器发送消息时发送到信鸽,信鸽再发送一次,广播接受器接受下: 我实现的功能比较简单,当app在运行状态时,会在主页展示一个弹窗展示推送的消息:如果app不在运行状态且service没被销毁就展示默认的通知 那么如何在主页展示弹窗:当广播接受器收到我要的消息时,用观察者模式,收到消息在发送个消息个主界面 官方的Demo连接:http://xg.qq.com/xg/help/ctr_

  • iOS 实现多代理的方法及实例代码

    iOS实现多代理 什么是多代理 用过环信SDK的同学应该对多代理不陌生了,请看下面代码: @method @brief 注册一个监听对象到监听列表中 @discussion 把监听对象添加到监听列表中准备接收相应的事件 @param delegate 需要注册的监听对象 @param queue 通知监听对象时的线程 @result */ - (void)addDelegate:(id<EMChatManagerDelegate>)delegate delegateQueue:(dispatc

随机推荐