RAC+MVVM 项目开发

2,153 阅读2分钟

登陆页面实现

普通MVC的处理方式,是所有的业务逻辑都交给控制器处理 在MVVM 架构中把控制吕的业务全部搬去VM模型,也就是每个控制器对应一个VM模型。

1. 控制器中初始化LoginViewModel并进行绑定

- (LoginViewModel *)loginViewModel{
    if (!_loginViewModel) {
        
        _loginViewModel = [[LoginViewModel alloc] init];
    }
    return _loginViewModel;
}

// 视图模型绑定
- (void)bindModel{

    // 给模型的属性绑定信号
    // 只要账号文本框一改变,就会给account赋值
    RAC(self.loginViewModel.account, account) =  _accountField.rac_textSignal;
    RAC(self.loginViewModel.account, pwd)     =  _pwdField.rac_textSignal;
    
    // 绑定登录按钮
    RAC(self.loginBtn, enabled) = self.loginViewModel.enableLoginSignal;
    
    // 监听登录按钮点击
    [[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
      
        // 执行登录事件
        [self.loginViewModel.LoginCommand execute:nil];
    }];
}

2. VC 中处理相应逻辑

@interface LoginViewModel : NSObject

@property (nonatomic, strong) Account *account;
@property (nonatomic, strong, readonly) RACSignal *enableLoginSignal;
@property (nonatomic, strong, readonly) RACCommand *loginCommand;

@end

初始化VM 时进行绑定

- (void)initialBind{

    // 监听账号的属性值改变,把他们聚合成一个信号。
    _enableLoginSignal = 
    [RACSignal combineLatest:@[RACObserve(self.account,account),RACObserve(self.account, pwd)]
                      reduce:^id(NSString *account,NSString *pwd){
        
         return @(account.length && pwd.length);
        
    }];
    
    // 监听登录产生的数据
    [_LoginCommand.executionSignals.switchToLatest subscribeNext:^(id x) {
       
        if ([x isEqualToString:@"登录成功"]) {
            NSLog(@"登录成功");
        }
    }];
     
    // 监听登录状态
    [[_LoginCommand.executing skip:1] subscribeNext:^(id x) {
        if ([x isEqualToNumber:@(YES)]) {
            
            [MBProgressHUD showMessage:@"正在登录..."];
            
        }else{
        
            [MBProgressHUD hideHUD];
        }
    }];
}


- (RACCommand*)loginCommand{

      if(!_loginCommand){
      
         _loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        
         return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
              // 模仿网络延迟
              dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
               
                [subscriber sendNext:@"登录成功"];
                // 数据传送完毕,必须调用完成,否则命令永远处于执行状态
                [subscriber sendCompleted];
            });
            
            return nil;
        }];
    }];
    return _loginCommand;
}

信号合并处理

combineLatest 将多个信号合并起来,并且拿到各个信号的最新的值,必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号(combineLatest 与 zipWith不同的是,每次只拿各个信号最新的值)

RAC(self.sureBtn,enabled) =

[RACSignal combineLatest:@[RACObserve(self.viewModel,name),RACObserve(self.viewModel, pwd)]
                  reduce:^(NSString *name, NSString *pwd){


    if (pwd.length > 0 && name.length > 0) {
    
        self.sureBtn.backgroundColor = UIColor.redColor;
        return @(YES);
        
    }else{
    
        self.sureBtn.backgroundColor = UIColor.systemGrayColor;
        return @(NO);
    }

}];

网络请求

控制器会提供一个请求的VM,处理界面的业务逻辑

1. 控制器中初始化RequestViewModel

- (RequestViewModel *)requesViewModel{

    if (!_requesViewModel) {
        _requesViewModel = [[RequestViewModel alloc] init];
    }
    return _requesViewModel;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.tableView.dataSource = self.requesViewModel;
    
    // 执行请求
    RACSignal *requesSiganl = [self.requesViewModel.reuqesCommand execute:nil];
   
   // 获取请求的数据
    [requesSiganl subscribeNext:^(NSArray *x) {
        
        self.requesViewModel.models = x;
        
        [self.tableView reloadData];
        
    }];
}

2. 视图模型

@interface RequestViewModel : NSObject<UITableViewDataSource>

// 请求命令
@property (nonatomic, strong, readonly) RACCommand *reuqesCommand;
//模型数组
@property (nonatomic, strong, readonly) NSArray *models;

@end
- (void)initialBind{

    _reuqesCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        
        //初始化信号
        RACSignal *requestSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
           
            
            NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
            parameters[@"q"] = @"基础";
            
            // 发送请求
            [[AFHTTPRequestOperationManager manager] GET:@"xxx" parameters:parameters success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {
                
             
                [subscriber sendNext:responseObject];
                
                [subscriber sendCompleted];
                
                
            } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
                
            }];
            
            return nil;
        }];
        
        
        
     
        // 在返回数据信号时,把数据中的字典映射成模型信号,传递出去
        return [requestSignal map:^id(NSDictionary *value) {
           
             NSMutableArray *dictArr = value[@"books"];
             
            // 字典转模型,遍历字典中的所有元素,全部映射成模型,并且生成数组
            NSArray *modelArr = [[dictArr.rac_sequence map:^id(id value) {
             
                return [Book bookWithDict:value];
                
            }] array];
            
            return modelArr;
        }];
        
    }];
 }
 
 #pragma mark - UITableViewDataSource

 ...