美文网首页
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 初体验

    一,创建工程,并用cocoapods安装了如下库 二,编写代码首先创建了如下的类 SJApiModel 将请求地址...

  • 2018-11-12

    iOS MVVM+RAC 从框架到实战 - 简书

  • 2019-12-10

    iOS MVVM+RAC 从框架到实战 http://www.cocoachina.com/articles/18659

  • yii初体验(7-15)

    yii初体验(7)视图 yii初体验(8)模块 yii初体验(9) 小部件widgets yii初体验(10) 前...

  • MVVM

    iOS MVVM+RAC 从框架到实战 【长篇高能】ReactiveCocoa 和 MVVM 入门 iOS 最全R...

  • 动画篇-layout动画初体验

    动画篇-layout动画初体验 动画篇-layout动画初体验

  • MVVM+RAC Demo

  • The “Swift Language Version” (SW

    今天尝试写一个MVVM+RAC的demo,项目中用pod引入ReactiveCocoa后运行报错:The “Swi...

  • 劳动主题画报

    一、实践画报(含体验日记) _____初体验(如:记者初体验、医生初体验等) 说明: 1.利用假期亲身体验一项工作...

  • 简约不简单

    初体验

网友评论

      本文标题:MVVM+RAC 初体验

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