美文网首页iOS程序猿iOS 学习iOS Developer
论RAC+MVVM 模式下的网络请求

论RAC+MVVM 模式下的网络请求

作者: 朱Younger | 来源:发表于2018-08-01 16:46 被阅读37次

    首先我简单的说一下,RAC、MVVM 是啥。

    RAC:即ReactiveCocoa 是一套函数式编程的框架,的确很好用,可以节省很多开发时间。当下Swift也提供很多类似的高阶函数,学习一下还是很有必要的,我这里给几个链接有兴趣的可以看一下。
    https://www.jianshu.com/p/87ef6720a096
    https://www.jianshu.com/p/e10e5ca413b7

    MVVM:由MVC演变而来的,为Controller 减负的设计模式。

    RAC在MVVM设计中,充当胶水的角色。

    1、数据绑定:作用View和ViewModel之间,但ViewModel中数据因为用户的操作变化时,View更改马上更新。
    2、网络请求: 把网络请求封装成Command,在View层触发网络访问
    3、Model 数据变化的时候,更新ViewModel数据

    逻辑关系请看下图:


    rac.png

    这里只是简单的描述了一下RAC+MVVM,下面重点介绍一下其中网络请求的部分。在实际开发过程中,网络请求部分与以前的block后调方式有细微的差别。我这里用信号的方式返回数据,View层,通过Command 命令订阅返回数据,这里封装成了一个工具:FMARCNetwork(https://github.com/zhufaming/ZFMRACNetwork)

    简单的思维导图:


    shiwei.png

    工具使用的主要三方框架:

    source 'https://github.com/CocoaPods/Specs.git'
    platform :ios, '8.0'
    
    target 'ZFMRACNetwork' do
       
        #AFN -->不解释
        pod 'AFNetworking'
     
        #ReactiveCoca  --> ReactiveCocoa 对OC的支持
        pod 'ReactiveObjC', '~> 3.1.0'
       
        #网络提示--> 网络监听显示,主要是展示状态
        pod 'JDStatusBarNotification', '~> 1.5.6'
       
        #提示 -->主要的提示库
        pod 'MBProgressHUD', '~> 1.1.0' 
     end
    

    项目结构:


    jiegou.png

    ZFMRACNetworkTool.h : 工具引用.h文件,使用工具,#import 一下就可以了
    FMHttpConstant.h 网络请求相关的宏定义
    FMHttpRequest.h 请求相关的参数配置
    FMHttpResonse.h 网络请求响应的,信号返回的数据模型
    FMARCNetwork 网络请求主要的工具类

    单例设计工具类:

    @interface FMARCNetwork : NSObject
    
    +(instancetype) sharedInstance;
    /**
     网络请求,返回信号
     按照, FMHttpRequest 参数化设置
     @param req FMHttpRequest
     @return RACSignal
     */
    - (RACSignal *)requestNetworkData:(FMHttpRequest *)req;
    
    /**
     网络请你,简便方案
    
     @param path 请求路径 --- 基本链接,请在 FMHttpRConstant.h 文件中设置
     @param params 参数字典
     @return RACSignal
     */
    - (RACSignal *)requestSimpleNetworkPath:(NSString *)path params:(NSDictionary *)params;
    
    
    /**
     文件上传、可以当个文件、也可以多个文件
    
     @param path 文件上传服务器地址,这里单独给出来,是因为很大部分图片服务器和业务服务器不是同一个
     @param params 参数 没有可传 @{}
     @param fileDatas NSData 数组
     @param name 指定数据关联的名称
     @return RACSignal
     */
    - (RACSignal *)uploadNetworkPath:(NSString *)path params:(NSDictionary *)params fileDatas:(NSArray<NSData *> *)fileDatas name:(NSString *)name mimeType:(NSString *)mimeType;
    
    
    @end
    
    

    方法简介:
    requestNetworkData : 通用请求,可以通过FMHttpRequest 配置请求的方式:Get or Post

    requestSimpleNetworkPath: 简单的网络请求,传url 当然是字符串了,字典参数,默认Post 请求。这样设计的目的其实现在很少有用get 的请求了。

    uploadNetworkPath: 文件或图片的上传。

    实现:

    - (RACSignal *)requestNetworkData:(FMHttpRequest *)req{
         /// request 必须的有值
        if (!req) return [RACSignal error:[NSError errorWithDomain:HTTPServiceErrorDomain code:-1 userInfo:nil]];
        
        @weakify(self);
        /// 创建信号
        RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
            @strongify(self);
            /// 获取request
            
            //如果需要 额外的参数,请追加到参数字典里面去
            //ExtendsParameters *exParams = req.extendsParameters;
            
            NSError *serializationError = nil;
            NSMutableURLRequest *request = [self.manager.requestSerializer requestWithMethod:req.method URLString:[[NSURL URLWithString:req.path relativeToURL:[NSURL URLWithString:BaseUrl] ] absoluteString] parameters:req.parameters error:&serializationError];
            
            if (serializationError) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.manager.completionQueue ?: dispatch_get_main_queue(), ^{
                    [subscriber sendError:serializationError];
                });
    #pragma clang diagnostic pop
                return [RACDisposable disposableWithBlock:^{
                }];
            }
            /// 获取请求任务
            __block NSURLSessionDataTask *task = nil;
            task = [self.manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                 @strongify(self);
                if (error) {
                    NSError *parseError = [self errorFromRequestWithTask:task httpResponse:(NSHTTPURLResponse *)response responseObject:responseObject error:error];
        
                    NSInteger code = [parseError.userInfo[HTTPServiceErrorHTTPStatusCodeKey] integerValue];
                    NSString *msgStr = parseError.userInfo[HTTPServiceErrorDescriptionKey];
                    //初始化、返回数据模型
                    FMHttpResonse *response = [[FMHttpResonse alloc] initWithResponseError:parseError code:code msg:msgStr];
                    //同样也返回到,调用的地址,也可处理,自己选择
                    [subscriber sendNext:response];
                    //[subscriber sendError:parseError];
                    [subscriber sendCompleted];
                    //错误可以在此处处理---比如加入自己弹窗,主要是服务器错误、和请求超时、网络开小差
                    [self showMsgtext:msgStr];
                    
                } else {
                  
                    /// 判断
                    NSInteger statusCode = [responseObject[HTTPServiceResponseCodeKey] integerValue];
                    if (statusCode == HTTPResponseCodeSuccess) {
                        FMHttpResonse *response = [[FMHttpResonse alloc] initWithResponseSuccess:responseObject[HTTPServiceResponseDataKey] code:statusCode];
                       
                        [subscriber sendNext:response];
                        [subscriber sendCompleted];
                        
                    }else{
                        if (statusCode == HTTPResponseCodeNotLogin) {
                            //可以在此处理需要登录的逻辑、比如说弹出登录框,但是,一般请求某个 api 判断了是否需要登录就不会进入
                            //如果进入可一做错误处理
                            NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                            userInfo[HTTPServiceErrorHTTPStatusCodeKey] = @(statusCode);
                            userInfo[HTTPServiceErrorDescriptionKey] = @"请登录!";
                            
                            NSError *noLoginError = [NSError errorWithDomain:HTTPServiceErrorDomain code:statusCode userInfo:userInfo];
                            
                            FMHttpResonse *response = [[FMHttpResonse alloc] initWithResponseError:noLoginError code:statusCode msg:@"请登录!"];
                            [subscriber sendNext:response];
                            [subscriber sendCompleted];
                            //错误提示
                            [self showMsgtext:@"请登录!"];
                            
                        }else{
                            NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                            userInfo[HTTPServiceErrorResponseCodeKey] = @(statusCode);
                            //取出服务给的提示
                            NSString *msgTips = responseObject[HTTPServiceResponseMsgKey];
                            //服务器没有返回,错误信息
                            if ((msgTips.length == 0 || msgTips == nil || [msgTips isKindOfClass:[NSNull class]])) {
                                msgTips = @"服务器出错了,请稍后重试~";
                            }
                            
                            userInfo[HTTPServiceErrorMessagesKey] = msgTips;
                            if (task.currentRequest.URL != nil) userInfo[HTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
                            if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
                            NSError *requestError = [NSError errorWithDomain:HTTPServiceErrorDomain code:statusCode userInfo:userInfo];
                            //错误信息反馈回去了、可以在此做响应的弹窗处理,展示出服务器给我们的信息
                            FMHttpResonse *response = [[FMHttpResonse alloc] initWithResponseError:requestError code:statusCode msg:msgTips];
                    
                            [subscriber sendNext:response];
                            [subscriber sendCompleted];
                            //错误处理
                            [self showMsgtext:msgTips];
                        }
                    }
                }
            }];
            
            /// 开启请求任务
            [task resume];
            return [RACDisposable disposableWithBlock:^{
                [task cancel];
            }];
        }];
        return [signal replayLazily]; //多次订阅同样的信号,执行一次
    }
    
    

    如何使用:给一个简单的例子,也在项目中,详情请git

    - (IBAction)correctAction:(UIButton *)sender {
        /// 1. 配置参数
        NSMutableDictionary *easyDict = [NSMutableDictionary dictionary];
        /// 2. 配置参数模型 #define MH_GET_LIVE_ROOM_LIST  @"Room/GetHotLive_v2"
        FMHttpRequest *req = [FMHttpRequest urlParametersWithMethod:HTTTP_METHOD_POST path:@"Room/GetHotLive_v2" parameters:easyDict];
        
        
        _reqSignal = [[FMARCNetwork sharedInstance] requestNetworkData:req];
        
        [_reqSignal subscribeNext:^(FMHttpResonse *response) {
            if (response.isSuccess) {
                NSLog(@"--%@",response.reqResult);
                
                NSData *jsonData = [NSJSONSerialization dataWithJSONObject:response.reqResult options:NSJSONWritingPrettyPrinted error:nil];
                
                NSString * str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
                
                self.textV.text = str;
            }
            
        }];
    }
    
    end@2x.png

    首先感谢大家浏览此文章。
    网络请求可以直接下载使用:如果对您有帮助,请求点赞或喜欢❤️❤️,如果你有个github 账号,请不吝给❤️,谢谢。

    相关文章

      网友评论

      本文标题:论RAC+MVVM 模式下的网络请求

      本文链接:https://www.haomeiwen.com/subject/bqhrvftx.html