在iOS开发过程中,如何为UIViewController瘦身是一个常见并且复杂的问题。为什么常见?如果没有精心的设计,UIViewController很容易变成很厚重、冗余。为什么复杂?在真实的项目中,都有各自的历史问题,要在现有的代码中瘦身是个非常有挑战的工作。
其实所谓的瘦身,核心就是要单一职责,这个跟面向对象的设计一样。像MVVM也是将之前UIViewController中处理的工作转换到其它的对象中。
在移动端开发的过程中,总是避免不了请求各种网络接口,这是移动App的特点。那么与网络接口交互的代码是一个界面的重要组成部分,这些部分完全可以独立成单独的层。而这些网络接口的请求和处理也都有自己的业务逻辑。
那么应该如何把网络接口交互的代码独立出来呢?我的答案是把单个接口封装成单独的类,这个类接收请求的参数,处理接口返回的结果。在PP的时候就已经在实践这种模式了,效果还不错。在最近的项目中,通过整合AFNetworking和Mantle这2大常用库,完成了最新的网络框架GQDataController,她可以自动的将接口返回的JSON转成Mantle对象。项目地址见:https://github.com/gonefish/GQDataController
下面我们将通过一个例子来描述如何使用GQDataController将网络请求处理的代码封装成单独的类。
要开始使用GQDataController,必须先创建自己的子类,就像你继承UIViewController一样。
.h文件
@interface BasicDataController : GQDataController @property (nonatomic, strong) NSString *ip; @end
你可以添加自定义的属性,用于保存接口返回的结果。
.m文件
@implementation BasicDataController - (NSArray *)requestURLStrings { return @[@"http://httpbin.org/ip"]; } - (void)handleWithObject:(id)object { [super handleWithObject:object]; self.ip = [object objectForKey:@"origin"]; } @end
requestURLStrings用于返回请求接口的地址,支持多个地址备用。子类重写handleWithObject:方法,并将返回的JSON对象赋值给实例属性。
在ViewController中创建实例,然后调用request开始请求(默认是GET请求,子类可以重写requestMethod来进行修改)最后通过Delegate方法处理结果,非常简单清晰的流程。
- (void)viewDidLoad { [super viewDidLoad]; self.basicDataController = [[BasicDataController alloc] initWithDelegate:self]; [self.basicDataController request]; } - (void)dataControllerDidFinishLoading:(GQDataController *)controller { self.label.text = [NSString stringWithFormat:@"IP: %@", self.basicDataController.ip]; }
对UIViewController来说,它并不需要知道请求的是什么接口和如何处理结果。这种模式与MVVM类似,但服务的对象稍微有点区别。所以,你可以在老代码中使用这种模式,也可以与ViewModel一起使用。
上面的例子只是GQDataController的基本使用,在对接口的结果处理上是采用手动保存的。GQDataController另外一个非常重要的功能是提供了对Mantle的支持,你可以自动的将返回的JSON结果转换成指定的Mantle对象。
你只需要在.m文件中添加如何代码
- (Class)mantleModelClass { return [IP class]; }
mantleModelClass返回用于转换成Mantle的类型,IP是一个Mantle子类。另外,子类也不需要重写handleWithObject了。
在ViewController中修改Delegate方法的内容
- (void)dataControllerDidFinishLoading:(GQDataController *)controller { self.label.text = [NSString stringWithFormat:@"IP: %@", self.basicDataController.mantleObject.origin]; }
GQDataController会将转换的结果保存在mantleObject和mantleObjectList中。如果转换的JSON是字典对象会保存在mantleObject中,如果是数组对象则会保存在mantleObjectList中。
@property (nonatomic, strong, nullable) __kindof MTLModel<MTLJSONSerializing> *mantleObject; @property (nonatomic, strong, nullable) NSMutableArray<__kindof MTLModel *> *mantleObjectList;
GQDataController还提供了分页、DataSource、接口重试、接口Stub的支持,更多使用可以参考项目的Demo。
通过使用GQDataController,强迫你把所有的接口都从UIViewController中分离出来,一方面减少了UIViewController的负担,另一方面也提高了接口对象的复用程度。而且,集成GQDataController非常简单,对现有代码模式影响非常小,你可以轻松的转换过来。
最近的这篇文章《 猿题库 iOS 客户端架构设计》也提到了类似的解决方案。