作为一款APP重要的组成部分,网络的请求和处理都有很大一部分的比重,对于这块的的操作是否便捷、好用,都会影响到前端的体验和自己敲代码时的心情。
前几天趁公司app送审得空,所以就彻底将网络这块重新封装了一下,为了后期拓展方便。昨天网站的攻击和解析等错误终于搞定了,所以趁今天星期就总结备忘一下。
该网络模块是基于AFNetworking封装的,当然后面如果替换为其他网络库也是很方便的,思路是一样的。
一、请求类型和返回结果类型
网络请求无外乎post请求和get请求,根据功能可以分为是上传文件,还是上传数据。下载内容也是分为是json数据,还是文件流类型,所以这里我列举了这几个功能类型。
///网络请求类型
typedef NS_ENUM(NSUInteger, YZNetToolRequestType) {
YZNetToolRequestTypePost = 0, //POST: 普通post请求,返回jsonData
YZNetToolRequestTypeGet, //GET: 普通get请求,返回jsonData
YZNetToolRequestTypeUploadFileAndData, //POST: 上传文件和数据,返回jsonData
YZNetToolRequestTypePostDownLoadFile, //POST: 带参数post下载数据或文件
YZNetToolRequestTypeGetDownLoadFile, //GET: 不带参数get下载数据或文件
YZNetToolRequestTypeUploadAndDownLoad //POST: 上传文件之后返回的数据是需要下载的文件流类型
};
二、发起请求函数的一致性
这次封装,将发起的请求函数封装为统一的一个函数,根据type的不同去执行不同的逻辑。
///开始请求网络
-(void)startRequestWithType:(YZNetToolRequestType)requestType andCompleteCallBack:(YZNetToolCompetionHandler)completion;
当然回调也是同一个
/**
网络请求完成的回调
@param response NSURLResponse
@param responseObject 如果是YZNetToolRequestTypeDownLoadFile和YZNetToolRequestTypeUploadAndDownLoad,返回的是NSURL *filePath文件路径,其他返回是jsonData
@param error 错误信息
*/
typedef void(^YZNetToolCompetionHandler)(NSURLResponse *response, id responseObject, NSError * error);
因为在普通app中,post请求json数据是最多的,所以可以再封装一个YZNetToolRequestTypePost的请求,这样就不用每次传这个类型了,这样主要是为了方便,不影响整个的一致性。
///使用YZNetToolRequestTypePost普通post请求,返回jsonData请求网络
-(void)startRequestWithCompleteCallBack:(YZNetToolCompetionHandler)completion
{
[self startRequestWithType:YZNetToolRequestTypePost andCompleteCallBack:completion];
}
当需要传值,或者上传文件的时候,只需要调用每个set函数,然后传不同的类型去请求即可,而不需要对应每个函数去调用不同的方法。
///设置请求的数据
-(void)setRequestData:(NSDictionary*)dataDic;
///添加要上传的文件数组
-(void)setYZNetToolMultipartFormData:(NSArray*)formDataArray;
2.1、为什么这么做?
首先从使用上来说,更有利于代码的整齐,一致的函数只需要关心怎么传类型,而不需要知道每个不同的请求的差异。举个例子:
//带参数post下载数据或文件
YZNetTool *aTools = [[YZNetTool alloc] initWithShortUrl:kYZUrl_Home_Watermark];
[aTools setRequestData:dic];
[aTools startRequestWithType:YZNetToolRequestTypePostDownLoadFile andCompleteCallBack:^(NSURLResponse *response, id responseObject, NSError *error) {
//...
}
];
//上传文件后下载
YZNetTool *aTools = [[YZNetTool alloc] initWithShortUrl:kYZUrl_Home_UploadUserInfo];
[aTools setRequestData:dic];
[aTools addYZNetToolMultipartFormData:formData];
[aTools startRequestWithType:YZNetToolRequestTypeUploadFileAndData andCompleteCallBack:^(NSURLResponse *response, id responseObject, NSError *error) {
//...
}];
虽然在AF这个库里面这两种函数的实现是不同的,但是在外层看起来,除了type不同,设置自己想要传的参数外,其他没有什么是不同的,这样即便是软件换另外一个人维护,也不至于分心去关注每个请求函数的调用方法。
在实现的方式上来说,两者是调用不同的方案,下面会说。而如果后期想更换网络请求库或者加功能,都可以很便捷的改动封装,而不需要去动整个项目工程。这样才是封装应有的目的。
三、拓展性
封装的时候一定要考虑到产品经理的脑洞!一定要考虑老板的脑洞!不要以为其他无关产品不是一个领域的,要知道很多东西只要有趣,老板都会想要的,很少会考虑适不适合的问题,适不适合是产品考虑的,但是做肯定是我们做的。
作为网络请求,基本上都会有这几个需求
- 网络请求的指示,比如悬浮窗。悬浮窗又要分为上传下载时是否显示进度、悬浮文字、悬浮标识等。
- 网络超时和断线重连的处理。
- 同步请求还是异步请求,是必须的请求还是额外的配置或者信息的收集。
- 用户无意的重复点击或者故意快速操作、暴力测试等
- 对应项目自己所做的加密或者防篡改标识
3.1、悬浮窗
在悬浮窗问题上面,我做了三个方面的设置,不显示、显示样式、延迟显示
///屏幕是否隐藏请求的旋转标识,默认为NO:显示请求标识
-(void)setHiddenProgressHUD:(BOOL)hidden;
///设置旋转的标识的显示文字,默认为旋转不带文字,显示请求标识时有效
-(void)setProgressHUDText:(NSString*)hudText;
///发起请求之后多少秒之后没回调才开始显示旋转标识,不设置的话,如果setHiddenProgressHUD为NO的时候,会立即显示旋转标识
-(void)setDelayShowProgressHUD:(float)timeDelay;
1、不显示
首先不显示的是为获取配置,后台传送报错信息以及刷新一些配置信息来使用的。这些操作并不强制非要等着数据传送成功,更甚的是并不一定非要传送成功,所以就不必显示,做到用户无感知即可。
2、显示样式
这类就是为了让用户感知,给他们操作的反馈,比如上传信息,上传资料。免得用户误以为什么都没有响应,以为软件出问题或者自己操作问题。
3、延迟显示
为什么延迟显示,这是因为某些操作或者某些界面并不需要让用户知道在请求信息,比如一个页面的刷新,但是如果再规定的时间内没有发生回调的时候,就需要显示标识,让用户知道不是页面没有反应。并且这也避免如果里面显示悬浮窗,而有些信息量很小的话,可能0.1s就返回又将悬浮窗关闭了,会让用户在视觉上有屏幕闪动一下的感觉。
这里延迟显示的实现的方案就是在请求的时候会加一个定时器,如果再规定的时间内没有返回数据,就会调用显示的函数,而如果返回了,则会取消该定时器。
///开始请求网络
-(void)startRequestWithType:(YZNetToolRequestType)requestType andCompleteCallBack:(YZNetToolCompetionHandler)completion
{
if (_timeDelayProgress>0.0f) {
if (!_requestTimer) {
_requestTimer = [NSTimer scheduledTimerWithTimeInterval:_timeDelayProgress target:self selector:@selector(startShowProgress) userInfo:nil repeats:NO];
}
}
...
3.2、网络超时和错误重连
在网络请求中,可设置网络超时的时间
///设置网络超时时间和重试次数,默认为10s,重试3次
-(void)setTimeoutInterval:(float)timeoutInterval andRetryCount:(int)retryCount
{
_timeoutInterval = timeoutInterval;
_retryCount = retryCount;
}
当网络发起请求的时候,设置配置中的超时时间,然后如果是发生错误的话,可以重新调用即可
- (void)startYZNetPostRequestWithURLStr:(NSString*)urlStr andRetryCount:(int)count andCallBack:(YZNetToolCompetionHandler)completion
{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
[configuration setTimeoutIntervalForRequest:_timeoutInterval];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
//...
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
DebugLog(@"接收数据:%@,%@",urlStr,responseObject);
STRONGSELF
if (error && retryCount>0) {
[strongSelf startYZNetPostRequestWithURLStr:urlStr andRetryCount:--retryCount andCallBack:strongSelf.completionHandler];
return;
}
//...
}];
//...
}
3.3、同步和异步
在需要用户的操作确认后才可以进行其他操作的,同步的就可以了,但是如果上传用户的错误信息,当然就如前面说的异步请求是个不错的选择,重新开一个线程进行就行了。
3.4、用户的操作
因为软件的用户各色各样,所以有很多好奇的或者其他的原因,会导致他们在上传或者其他耗时操作时故意点击其他区域,这时就要对一部分操作进行限制。
如我们的app是一定要等用户信息后台有反馈之后才可以进行操作,那么就完全可以在发起网络请求时禁止到一切屏幕的点击。禁止的方法有很多,这里说一种,可以在window上面添加一个遮罩层,在网络请求结束时去掉这个遮罩层,这个遮罩层的主要作用就是屏蔽也就是吞噬到一切点击触摸事件。当然这个遮罩层也可以设置颜色,这样就不用区分不同的界面去耗尽心思去屏蔽navigationbar和tabbar的点击操作了。
3.5、传输的标识和加密
传输时,为了防止信息截取,或者估计篡改伪造数据,所以最好自己的数据在前后端都可以做一致性的标识判断或者加密处理。这方面是必须的,所以在考虑拓展的时候也需要考虑到,这样在后期使用的时候就可以不用重复修改。
四、稳定性
在之前写过的颜值*聘2.3.2iOS版本逻辑优化实录中曾经提到了网络接收数据的检测,这个就是一个稳定性的一个方面。
因为用户的网络条件是不同的,当你在使用百兆wifi网络的时候,也许另外一个用户正在使用3g移动网络或者更糟糕的网络,又或者服务器的抽风,所以在接收数据的时候,一定要验证数据为空时的处理。只有当数据符合格式,才去更新数据,不然不符合或者为空的数据,轻者导致界面显示的错误,重者可能直接就导致软件崩溃闪退。
再有就是采取稳定的网络库,良好的兼容性和可保证的维护性,比如之前的asi就停止了维护。所以一个好用的,稳定良好的请求库也是一个很好的保证。
五、总结
最后就总结下,因为基本上企业软件都不会是一个单机软件,所以网络构成会很重要,做好这方面会受益良多。当然这个整篇内容都是按照我自己的想法来的,有其他建议,希望可以听到你的声音,大家共同学习,共同进步。
版权属于:东哥笔记 - DongGe.org
本文链接:https://dongge.org/blog/691.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!