iOS函数式编程iOS开发实录iOS 开发

ReactiveCocoa+MVVM实战

2016-09-02  本文已影响436人  lixuCoding

ReactiveCocoa是由github开发维护的一个开源框架,简称RAC,它采用的是函数响应式编程(FRP)技术,区别于Objective-c面相对象的编程思想。所以刚接触这类编程思想的理解起来会有一点变扭。

在iOS中使用RAC后代码可读可维护,结构清晰,RAC提供的事件流通过 Signal 和 SignalProducer 类型来表示, 统一了Cocoa用于事件和异步处理的常用模式,包括

RAC语法如下:

@weakify(self);
[[[[[[[self requestAccessToTwitterSignal]
      then:^RACSignal *{
          @strongify(self)
          return self.searchText.rac_textSignal;
      }]
     filter:^BOOL(NSString *text) {
         @strongify(self)
         return [self isValidSearchText:text];
     }]
    throttle:0.5]
   flattenMap:^RACStream *(NSString *text) {
       @strongify(self)
       return [self signalForSearchWithText:text];
   }]
    deliverOn:[RACScheduler mainThreadScheduler]]
    subscribeNext:^(NSDictionary *jsonSearchResult) {
       NSArray *statuses = jsonSearchResult[@"statuses"];
       NSArray *tweets = [statuses linq_select:^id(id tweet) {
           return [RWTweet tweetWithStatus:tweet];
     }];
     [self.resultsViewController displayTweets:tweets];
 } error:^(NSError *error) {
     NSLog(@"An error occurred: %@", error);
 }];

这个例子就不用解释了,往下看会明白,其中所有的API都基于信号Signal的,常用API如下:

1.绑定
2.映射
3.组合
4.过滤
5.秩序
6.线程
7.时间
8.重复

下面基于RAC+MVVM写了一个demo,源码请点击github地址下载。效果图如下:

image1.png

项目结构图如下:


image.png

M:的代码

//UserModel.h
@interface UserModel : NSObject
@property (nonatomic, copy)   NSString *desc;
@property (nonatomic, copy)   NSString *name;
@property (nonatomic, copy)   NSString *avatar;
@property (nonatomic, copy)   NSString *alt;
+ (instancetype)userWithDict:(NSDictionary *)dic;
@end

//UserModel.m
@implementation UserModel
+ (instancetype)userWithDict:(NSDictionary *)dic
{
    //采用的笨方法选择需要的几个数据
    UserModel *book = [[UserModel alloc] init];
    book.name = dic[@"name"];
    book.desc = dic[@"desc"];
    book.avatar = dic[@"avatar"];
    book.alt = dic[@"alt"];
    return book;
}
@end

model里的代码很简单,仅有的一个方法就是字典转模型,没有用kvc与第三方转化。

V:的代码(V包括View与ViewController)

#import "ViewController.h"
#import "RequestViewModel.h"
#import "DetailController.h"

@interface ViewController ()

@property (nonatomic, weak)   UITableView *tableView;
@property (nonatomic, strong) RequestViewModel *requesViewModel;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.title = @"RAC + MVVM";
    self.view.backgroundColor = [UIColor whiteColor];
    _requesViewModel = [[RequestViewModel alloc] init];
    
    UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
    tableView.dataSource = self.requesViewModel;
    tableView.delegate = self.requesViewModel;
    [self.view addSubview:tableView];
    self.tableView = tableView;
    
    _requesViewModel.selectCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(RACTuple *turple) {
        
        DetailController *detailVC = [[DetailController alloc] init];
        detailVC.sendObject = turple;
        [self.navigationController pushViewController:detailVC animated:YES];
        return [RACSignal empty];
    }];
    
    @weakify(self);
    [[[self.requesViewModel.reuqesCommand execute:nil]
     deliverOn:[RACScheduler mainThreadScheduler]]
     subscribeNext:^(NSArray *x) {
         @strongify(self);
         self.requesViewModel.models = x;
         [self.tableView reloadData];
    }];
}

@end

首先实例话了一个VM对象_requesViewModel,然后创建UI,将代理方法都设置为vm对象,从而减少了C代码量。接着绑定selectCommand,这个命令实现跳转控制器。最后执行reuqesCommand,通过execute方法激活信号,通过deliverOn方法转移到主线程用于刷新UI,然后通过subscribeNext方法订阅了信号,传的block参数需要发送sendNext方法才会被激活。其中涉及到RACTuple类,它就是集合类,这里做了NSArray的事。

VM:的代码


#import "RequestViewModel.h"
#import "UserModel.h"
#import "AFNetWorkHelp.h"
#import "LXTableViewCell.h"

@implementation RequestViewModel

- (instancetype)init
{
    if (self = [super init]) {
        [self initialBind];
    }
    return self;
}

- (void)initialBind
{
    _reuqesCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        
        RACSignal *requestSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
            parameters[@"q"] = @"iOS";
            parameters[@"count"] = @"30";
            NSString *urlStr = @"https://api.douban.com/v2/user";
            
            [AFNetWorkHelp getWithUrlString:urlStr withParam:parameters success:^(NSDictionary *responseDic) {
                
                // 请求成功调用
                // 把数据用信号传递出去
                [subscriber sendNext:responseDic];
                [subscriber sendCompleted];
                LogBlue(@"%@", responseDic);

            } failure:^(NSError *error) {
                
            }];
            
            return nil;
        }];
        
        // 在返回数据信号时,把数据中的字典映射成模型信号,传递出去
        return [requestSignal map:^id(NSDictionary *value) {
            NSArray *dictArr = value[@"users"];
            
            // 字典转模型,遍历字典中的所有元素,全部映射成模型,并且生成数组
#if 1       
            //这是正常方法
            NSArray *modelArr = [dictArr dicToModel:^id(id user) {
                return [UserModel userWithDict:user];
            }];
#else
            //这是ARC提供的遍历方法,简单
            NSArray *modelArr = [[dictArr.rac_sequence map:^id(id value) {
                return [UserModel userWithDict:value];
            }] array];
#endif
            return modelArr;
        }];
    }];
}

#pragma mark - UITableViewDelegate
//省略部分代理方法
......
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    RACTuple *turple = [RACTuple tupleWithObjects:self.models[indexPath.row], indexPath, nil];
    [self.selectCommand execute:turple];
}

@end

初始化本类的对象时就创建了_reuqesCommand信号,用于暴露给V去激活。在这个信号的block中做了两件事,第一是用信号requestSignal执行网络请求,套用了AFNetworking框架。第二是在return前将得到的网络数据转化为模型数组。

当获取到网络数据运行[subscriber sendNext:responseDic];是发送信号,后才会执行return [requestSignal map:^id(NSDictionary *value)后的block,收到信号。



源码请点击github地址下载。


QQ:2239344645 我的github

上一篇下一篇

猜你喜欢

热点阅读