美文网首页
MVVM+RAC 初体验

MVVM+RAC 初体验

作者: 若非长得丑怎会做逗比 | 来源:发表于2016-06-28 00:39 被阅读803次

    一,创建工程,并用cocoapods
    安装了如下库

    pod 'ReactiveCocoa', '~>2.5'
    pod 'AFNetworking', '~> 3.1.0'
    pod 'MJExtension', '~> 3.0.9'
    pod 'MJRefresh', '~> 3.1.0'
    

    二,编写代码
    首先创建了如下的类

    Markdown
    • SJApiModel 将请求地址,参数,模型类,回调封装成一个Model
    @interface SJApiModel : NSObject
    ​
    @property (nonatomic, strong, readonly) SJApiHelper * _Nonnull manager;
    /**
     * 请求地址
     */
    @property(nonatomic,strong) NSString * _Nonnull url ;
    /**
     * 请求参数
     */
    @property(nonatomic,strong) NSMutableDictionary * _Nullable params ;
    /**
     * 成功回调
     */
    @property(nonatomic,strong) void(^ _Nullable successfulBlock)(id _Nullable obj);
    ​
    /**
     * 错误回调
     */
    @property(nonatomic,strong) void(^ _Nullable errBlock)(NSError * _Nullable error);
    ​
    /**
     * 模型的类
     */
    @property(nonatomic,strong) Class _Nullable cls;
    /**
     * 请求方法
     */
    @property(nonatomic,assign) RequestMethod method;
    /**
     * 请求
     */
    - (NSURLSessionTask * _Nullable)request;
    
    @end
    
    • SJApiModel 实现文件
    @interface SJApiModel ()
    @property (nonatomic, strong) SJApiHelper *manager;
    @property (nonatomic, strong) AFNetworkReachabilityManager *statusManager;
    ​
    @property (nonatomic, strong) NSURLSessionTask *currentOperation;
    @end
    ​
    @implementation SJApiModel
    ​
    - (SJApiHelper *)manager {
       
       if (!_manager) {
           _manager = [[SJApiHelper alloc] init];
       }
       return _manager;
    }
    ​
    - (AFNetworkReachabilityManager *)statusManager {
       if (!_statusManager) {
           _statusManager = [AFNetworkReachabilityManager sharedManager];
       }
       return _statusManager;
    }
    - (instancetype)init {
       if (self == [super init]) {
           _currentOperation = nil;
           self.params = [NSMutableDictionary dictionary];
           self.method = RequestMethodGET;
       }
       return self;
    }
    - (NSURLSessionTask *)request {
       
       if (self.statusManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
         // 如果没有联网的情况我们直接 return
    
           return nil;
       }
       @weakify(self);
     // 通过 SJApiHelper 发起请求 并将返回的 Task 保存起来
    
       self.currentOperation = [self.manager requestWirlApiURLString:self.url
                                                               params:self.params
                                                               method:self.method success:^(id _Nullable obj) {
                                                                   @strongify(self);
    // 请求成功 将请求的到数据转换成模型然后再传递出去                                                               [self createModel:obj];
    
                                                               } failure:^(NSError * _Nullable error) {
                                                                   @strongify(self);
                                                                   !self.errBlock ?: self.errBlock(error);
                                                               }];
       
       return nil;
    }
    ​
    - (void)createModel:(id)responseObject {
       // 首先判断数据是否有值
    
       if (!responseObject) {
           !self.successfulBlock ?: self.successfulBlock(nil);
           return;
       }
     // 在判断是否设置了模型类
    
       if (self.cls) {
    
           id obj;
         // 如果有设置 模型类 则 利用字典转模型框架转换为对应的模型 并回调出去
    
           if ([responseObject isKindOfClass:[NSDictionary class]]) {
               obj = [self.cls mj_objectWithKeyValues:responseObject];
           } else if ([responseObject isKindOfClass:[NSArray class]]) {
               obj = [self.cls mj_objectArrayWithKeyValuesArray:responseObject];
           }
    
         // 回主线程回调
    
           dispatch_async(dispatch_get_main_queue(), ^{
               !self.successfulBlock ?: self.successfulBlock(obj);
           });
       } else {
         // 如果没有设置 模型类 则传递原始数据
    
           !self.successfulBlock ?: self.successfulBlock(responseObject);
       }
    }
    -(void)dealloc{
    
       [self.currentOperation cancel];
    
    }
    
    @end
    
    • SJApiHelper 通过这个类来发起请求,在头文件声明了一个请求的API
    @interface SJApiHelper : NSObject
    ​
    /**
     * 网络请求
     *
     * @param apiURLString 请求的后缀地址
     * @param params       请求参数
     * @param method       请求类型
     * @param success     请求成功的回调
     * @param failure     请求失败的回调
     */
    - (NSURLSessionTask*)requestWirlApiURLString:(NSString*)apiURLString
                                         params:(NSDictionary*)params
                                         method:(RequestMethod)method
                                         success:(SuccessfulBlock)success
                                         failure:(ErrBlock)failure;
    
    @end
    
    • 再来看看这个类的实现文件
    // 这只是简单版
    // 可以在这里可以配置网络请求 
    
    // 列如加载证书, 设置请求,响应的序列化格式,请求超时等等
    
    // 等等.....
    
    @interface SJApiHelper()
    
    @property(nonatomic,strong)AFHTTPSessionManager *manager;
    ​
    @end
    ​
    @implementation SJApiHelper
    ​
    - (instancetype)init {
       
       if (self == [super init]) {
           self.manager = [AFHTTPSessionManager manager];
       }
       return self;
    }
    ​
    - (NSURLSessionTask *)requestWirlApiURLString:(NSString *)apiURLString params:(NSDictionary *)params method:(RequestMethod)method success:(SuccessfulBlock)success failure:(ErrBlock)failure {
       if (method == RequestMethodGET) {
           return [self.manager GET:apiURLString
                         parameters:params
                           progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
                               success(responseObject);
                           } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                               failure(error);
                           }];
       } else if (method == RequestMethodPOST) {
           return [self.manager POST:apiURLString
                           parameters:params
                             progress:nil
                             success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
                                 success(responseObject);
           } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                               failure(error);
           }];
       } else {
       
       return nil;
       }
    }
    @end
    ​```
    
    - `SJBaseViewModel` 声明文件.支持链式调用
    
    ```​objective-c
    /** 通知界面刷新 */
    @interface SJVMNotificationObject : NSObject
    /**
     * 刷新的类型,类型的枚举自己定义,枚举类型是NSInteger
     */
    @property(nonatomic,assign) NSInteger notificationType;
    /**
     * 刷新时带的数据
     */
    @property(nonatomic,strong) id object;
    ​
    /**
     * 其它刷新时带的数据
     */
    @property(nonatomic,strong) id otherObject;
    ​
    //生成一个只有通知类型的简单信号通知
    + (SJVMNotificationObject*)createWithNotionType:(NSInteger)type;
    @end
    ​
    @class ApiModel;
    ​
    @interface SJBaseViewModel : NSObject
    // ViewModel里面刷新数据
    @property (nonatomic, strong) UITableView *tableView ;
    // 通知界面刷新的信号
    @property(nonatomic,strong) RACSignal *signal;
    /**
     * 创建ViewModel,初始化网络请求对象
     */
    + (SJBaseViewModel *(^)())create;
    /**
     * 设置请求的地址
     */
    - (SJBaseViewModel *(^)(NSString* url))setUrl;
    /**
     * 设置请求方法
     */
    - (SJBaseViewModel *(^)(RequestMethod method))setMethod;
    /**
     * 增加请求的参数
     */
    - (SJBaseViewModel *(^)(NSDictionary* params))addParams;
    /**
     * 设置返回对象类型
     */
    - (SJBaseViewModel *(^)(Class cls))setResponseClass;
    /**
     * 设置网络请求回调函数
     */
    - (SJBaseViewModel *(^)(void(^block)(id obj)))setRequestBlock;
    /**
     * 请求
     */
    - (SJBaseViewModel *(^)())request;
    ​
    /**
     * 通知界面刷新数据
     */
    - (void)refreshNotification:(SJVMNotificationObject *)obj;
    /**
     * 没有设置block的情况下提供一个默认的方法来处理网络请求的回调
     *
     * @param obj 网络请求后的数据
     */
    - (void)dataResponse:(id)obj;
    ​
    
    //销毁 可以做一些清理操作
    
    - (void)destory;
    ​
    @end
    
    • 实现文件
    @implementation SJVMNotificationObject
    ​
    + (SJVMNotificationObject*)createWithNotionType:(NSInteger)type
    {
       SJVMNotificationObject* obj = [[SJVMNotificationObject alloc] init] ;
       obj.notificationType = type ;
       return obj ;
    }
    ​
    @end
    ​
    @interface SJBaseViewModel ()
    ​
    @property(nonatomic,strong) SJApiModel *apiModel ;
    @property(nonatomic,strong) id<RACSubscriber> subscriber;
    @end
    @implementation SJBaseViewModel
    ​
    + (SJBaseViewModel *(^)())create
    {
       return ^{
           SJBaseViewModel* vm = [[SJBaseViewModel alloc] init] ;
           vm.apiModel = [[SJApiModel alloc] init] ;
           vm.apiModel.cls = nil;
           return vm ;
       };
    }
    ​
    - (void)dealloc
    {
       
    }
    ​
    - (instancetype)init
    {
       self = [super init] ;
       if(self)
       {
           [self setup];
           
       }
       return self ;
    }
    ​
    - (void)setup
    {
       self.apiModel = [[SJApiModel alloc] init] ;
       @weakify(self);
       // 创建信号 以及订阅者
       self.signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
           @strongify(self);
           self.subscriber = subscriber ;
           return [RACDisposable disposableWithBlock:^{
           }];
       }];
       // 设置回调
       self.apiModel.successfulBlock = ^(id obj){
           @strongify(self);
           [self dataResponse:obj] ;
       };
    }
    - (SJBaseViewModel *(^)(NSString* url))setUrl
    {
       return ^(NSString* url){
           self.apiModel.url = url ;
           return self ;
       };
    }
    ​
    - (SJBaseViewModel *(^)(RequestMethod method))setMethod
    {
       return ^(RequestMethod method){
           self.apiModel.method = method ;
           return self ;
       };
    }
    - (SJBaseViewModel *(^)(NSDictionary* params))addParams
    {
       return ^(NSDictionary* params){
           [self.apiModel.params addEntriesFromDictionary:params] ;
           return self ;
       };
    }
    - (SJBaseViewModel *(^)(Class cls))setResponseClass
    {
       return ^(Class cls){
           self.apiModel.cls = cls;
           return self ;
       };
    }
    ​
    - (SJBaseViewModel *(^)(void(^block)(id obj)))setRequestBlock
    {
       return ^(void(^block)(id obj)){
           self.apiModel.successfulBlock = block ;
           return self ;
       };
    }
    ​
    - (SJBaseViewModel *(^)())request
    {
       return ^{
           [self.apiModel request];
           return self;
       };
    }
    - (void)refreshNotification:(SJVMNotificationObject *)obj
    {
       @weakify(self);
       dispatch_async(dispatch_get_main_queue(), ^{
           @strongify(self);
    
           // 通过订阅者发送一个消息 并携带一个数据
    
           [self.subscriber sendNext:obj];
       });
    }
    ​
    - (void)dataResponse:(id)obj
    {
       //子类来重载实现
    }
    ​
    - (void)destory
    {
       //子类重载
    }
    @end
    

    三,实践

    • 首先用SB 搭建一个界面,并连好引用


      Markdown
    • 然后新建一个 继承自SJBaseViewModelViewModel

    #import "SJBaseViewModel.h"
    ​
    // 定义通知类型
    typedef NS_OPTIONS(NSUInteger, SJSearchViewModelType) {
       SJSearchViewModelTypeSucces = 0,
    };
    ​
    @interface SJSearchViewModel : SJBaseViewModel
    ​
    // 加载数据
    - (void)loadDataWithSearcText:(NSString *)text;
    ​
    @end
    ​
    // 实现文件
    #import "SJSearchViewModel.h"
    #import "GitHubModel.h"
    ​
    @implementation SJSearchViewModel
    ​
    - (void)loadDataWithSearcText:(NSString *)text {
       
       self.setUrl(@"https://api.github.com/search/repositories")
       .addParams(@{@"q" : text})
       .setResponseClass([GitHubModel class]) // 设置模型类
       .setRequestBlock(^(GitHubModel *obj){ 
       // 请求成功的回调就是模型
           if (obj) {
           // 创建一个通知 并绑定类型以及需要携带的数据
               SJVMNotificationObject *notObj = [[SJVMNotificationObject alloc] init];
               notObj.notificationType = SJSearchViewModelTypeSucces;
               notObj.object   = obj;
    
               // 更新通知
    
               [self refreshNotification:notObj];
           }
       })
       .request();
    }
    @end
    
    • 新建一个 GitHubModel模型类
    #import <Foundation/Foundation.h>
    ​
    @interface GitHUbInfoModel : NSObject
    @property (nonatomic, strong) NSString *ID;
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, strong) NSString *full_name;
    @end
    ​
    @interface GitHubModel : NSObject
    @property (nonatomic, assign) NSInteger total_count;
    @property (nonatomic, strong) NSArray<GitHUbInfoModel *> *items;
    @end
    ​
    ​
    #import "GitHubModel.h"
    #import <MJExtension/MJExtension.h>
    ​
    ​
    @implementation GitHUbInfoModel
    ​
    + (void)load {
       
       [GitHUbInfoModel mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
           return @{@"ID" : @"id"};
       }];
    }
    @end
    ​
    @implementation GitHubModel
    ​
    + (void)load {
       
       [GitHubModel mj_setupObjectClassInArray:^NSDictionary *{
           return @{@"items" : [GitHUbInfoModel class]};
       }];
    }
    
    @end
    
    • 控制器代码
    #import "ViewController.h"
    #import <ReactiveCocoa/ReactiveCocoa.h>
    #import "SJSearchViewModel.h"
    #import "GitHubModel.h"
    ​
    @interface ViewController ()<UITableViewDataSource>
    @property (weak, nonatomic) IBOutlet UITextField *inpuView;
    @property (weak, nonatomic) IBOutlet UIButton *searchBtn;
    @property (weak, nonatomic) IBOutlet UITableView *tableView;
    @property (nonatomic, strong) SJSearchViewModel *viewModel;
    @property (nonatomic, strong) NSArray<GitHUbInfoModel *> *dataSouces;
    @end
    ​
    @implementation ViewController
    ​
    - (void)viewDidLoad {
       [super viewDidLoad];
       self.searchBtn.enabled = NO;
       
       RAC(self.searchBtn, enabled) = [RACSignal combineLatest:@[self.inpuView.rac_textSignal] reduce:^id(NSString *text){
           return text.length >= 5 ? @(YES) : @(NO);
       }];
       @weakify(self);
       [[self.searchBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
           @strongify(self);
           [self loadData];
       }];
       self.viewModel = [[SJSearchViewModel alloc] init];
       // 通过ViewModel 的 signal 监听 数据更新的通知
       [self.viewModel.signal subscribeNext:^(SJVMNotificationObject *x) {
           @strongify(self);
           if (x.notificationType == SJSearchViewModelTypeSucces) {
               [self dataChange:x.object];
           }
       }];
       self.dataSouces = [NSArray array];
       [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    }
    ​
    - (void)loadData {
       
       NSLog(@"请求数据.....");
       [self.inpuView resignFirstResponder];
       // 调用ViewModel 请求数据
       [self.viewModel loadDataWithSearcText:self.inpuView.text];
    }
    ​
    - (void)dataChange:(GitHubModel *)obj {
    ​
       if (obj) {
       // 请求到数据 然后展示
           self.dataSouces = obj.items;
           [self.tableView reloadData];
       }
       
    }
    ​
    #pragma mark ------------------------------------
    #pragma mark UITableViewDataSource
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
       return self.dataSouces.count;
    }
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
       
       UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
       cell.textLabel.text = self.dataSouces[indexPath.row].name;
       return cell;
    }
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
       
       return [NSString stringWithFormat:@"搜索到 %ld 条数据", self.dataSouces.count];
    }
    @end
    

    四,最终的效果

    Markdown
    我的微博
    我的小站
    GitHub源码

    相关文章

      网友评论

          本文标题:MVVM+RAC 初体验

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