項目中學習ReactiveCocoa的使用方法

1、註冊控制器ide

控制器上的一個屬性post

@property (weak, nonatomic) IBOutlet UIBarButtonItem *signInBtn;

fetch

viewDidLoad

方法中寫入atom

self.signInBtn.rac_command = self.viewModel.executeSignInCommand;

self.viewModel是控制器上的一個viewModelspa

@property (nonatomic, weak) MSFAuthorizeViewModel *viewModel;

其上有個屬性code

@property (nonatomic, strong) RACCommand *executeSignInCommand;

這個executeSignInCommand在viewModel的初始化方法中生成blog

- (instancetype)initWithServices:(id <MSFViewModelServices>)services {
    
   self = [super init];
    if (!self) {
        return nil;
    }
    _services = services;

       .....
   
       _executeSignInCommand = [[RACCommand alloc]      
        initWithSignalBlock:^RACSignal *(id input) {
        
               self.loginType = MSFLoginSignIn;
        [self.services presentViewModel:self];
        return [RACSignal return:nil];
    }];
       ....
}

而presentViewModel:僅僅只是一個協議,沒有實現,有個樣本input

- (void)presentViewModel:(id)viewModel {
    id viewController;
    
    if ([viewModel isKindOfClass:MSFAuthorizeViewModel.class]) {
        MSFAuthenticateViewController *loginViewController = [[MSFAuthenticateViewController alloc] initWithViewModel:viewModel];
        viewController = [[UINavigationController alloc] initWithRootViewController:loginViewController];
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(UIScreen.mainScreen.bounds), 20)];
        view.backgroundColor = UIColor.blackColor;
        [[(UIViewController *)viewController view] addSubview:view];
    } else {
        NSLog(@"an unknown ViewModel was present!");
    }
    
    [self.navigationController presentViewController:viewController animated:YES completion:nil];
}

再看看控制器的其餘屬性同步

@property (nonatomic, weak) IBOutlet UITextField *usernameF;
@property (nonatomic, weak) IBOutlet UITextField *captchaF;
@property (nonatomic, weak) IBOutlet UITextField *passwordF;

也是在viewDidLoad中處理的,方法viewWillAppear有一執行就會有一個信號發送,控制器訂閱了這個信號,並在收到信號後,執行操做:string

把控件上的文件賦值給viewModel上的對應的屬性。

     @weakify(self)
    [[self rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(id x) {
        @strongify(self)
        self.viewModel.username = self.usernameF.text;
        self.viewModel.password = self.passwordF.text;
        self.viewModel.loginType = MSFLoginSignUp;
    }];

固然,相應的textField文本改變時,咱們也會處理

輸入的文本長度超過規定的長度,則進行截取而後放置到field中,再賦值給viewModel中對應的屬性

[self.username.rac_textSignal subscribeNext:^(id x) {
        @strongify(self)
        if ([x length] > MSFAuthorizeUsernameMaxLength) self.username.text = [x substringToIndex:MSFAuthorizeUsernameMaxLength];
        self.viewModel.username = self.username.text;
    }];

密碼輸入框也是

[self.password.rac_textSignal subscribeNext:^(id x) {
        @strongify(self)
        if ([x length] > MSFAuthorizePasswordMaxLength) self.password.text = [x substringToIndex:MSFAuthorizePasswordMaxLength];
        self.viewModel.password = self.password.text;
    }];

還有密碼field在按下下一步的return鍵時,就至關於按下了註冊按鈕,此時就會調用viewModel上的executeSignUp命令

[self.password.rac_keyboardReturnSignal subscribeNext:^(id x) {
        @strongify(self)
        [self.viewModel.executeSignUp execute:nil];
    }];

這個命令也是跟登陸命令同樣在同一個方法中初始化

_executeSignUp = [[RACCommand alloc] initWithEnabled:self.signUpValidSignal
        signalBlock:^RACSignal *(id input) {
            @strongify(self)
         
            return [self executeSignUpSignal];
        }];

固然,這個命令要受到能點不能點擊的影響self.signUpValidSignal,當用戶名和密碼還有驗證碼都有輸入的狀況下,就會調用自身上的一個excuteSignUpSignal

- (RACSignal *)signUpValidSignal {
    return [RACSignal
        combineLatest:@[
            RACObserve(self, username),
            RACObserve(self, password),
            RACObserve(self, captcha),
        ]
        reduce:^id(NSString *username, NSString *password, NSString *captcha){
            return @(username != nil && password != nil && captcha != nil);
        }];
}

忽然間感受這裏好複雜再也不深究,先看其餘的

- (RACSignal *)executeSignUpSignal {
    if (![self.username isMobile]) {
        return [RACSignal error:[self.class errorWithFailureReason:@"請填寫真實的手機號碼"]];
    } else if (![self.password isPassword]) {
        return [RACSignal error:[self.class errorWithFailureReason:@"請填寫8到16位數字和字母組合的密碼"]];
    } else if (![self.captcha isCaptcha]) {
        return [RACSignal error:[self.class errorWithFailureReason:@"請填寫驗證碼"]];
    } else if (!self.agreeOnLicense) {
        return [RACSignal error:[self.class errorWithFailureReason:@"請閱讀註冊協議"]];
    }
    
    MSFUser *user = [MSFUser userWithServer:MSFServer.dotComServer];
    return [[MSFClient
        signUpAsUser:user password:self.password phone:self.username captcha:self.captcha]
        doNext:^(MSFClient *client) {
            _signInValid = YES;
            [self.services setHttpClient:client];
            [[client fetchUserInfo] subscribeNext:^(MSFUser *x) {
                [client.user mergeValueForKey:@keypath(x.personal) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.professional) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.contacts) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.profiles) fromModel:x];
                [client.user mergeValueForKey:@keypath(x.insurance) fromModel:x];
            }];
        }];
}

有個顯示密碼與隱藏密碼的按鈕

[[self.showPasswordButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        @strongify(self)
        self.showPasswordButton.selected = !self.showPasswordButton.selected;
        NSString *text = self.password.text;
        self.password.text = text;
        self.password.enabled = NO;
        [self.password setSecureTextEntry:!self.showPasswordButton.selected];
        self.password.enabled = YES;
        [self.password becomeFirstResponder];
    }];

再看看驗證碼輸入框,長度限制、截取、同步viewModel的對應屬性

[self.captcha.rac_textSignal subscribeNext:^(id x) {
        @strongify(self)
        if ([x length] > MSFAuthorizeCaptchaMaxLength) self.captcha.text = [x substringToIndex:MSFAuthorizeCaptchaMaxLength];
        self.viewModel.captcha = self.captcha.text;
    }];

同步viewModel上的命令

    self.sendCaptchaButton.rac_command = self.viewModel.executeCaptcha;

執行命令時的操做,鍵盤釋放,SVProgressHUD顯示正在獲取驗證碼

[self.sendCaptchaButton.rac_command.executionSignals subscribeNext:^(RACSignal *captchaSignal) {
        @strongify(self)
        [self.view endEditing:YES];
        [SVProgressHUD showWithStatus:@"正在獲取驗證碼" maskType:SVProgressHUDMaskTypeClear];
        [captchaSignal subscribeNext:^(id x) {
            [SVProgressHUD dismiss];
        }];
    }];

當這個命令有錯誤時的回調

[self.sendCaptchaButton.rac_command.errors subscribeNext:^(NSError *error) {
        [SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedFailureReasonErrorKey]];
    }];

同理,提交、註冊按鈕也是相似 的,先綁定命令

    self.commitButton.rac_command = self.viewModel.executeSignUp;

而後,執行命令的回調,釋放鍵盤,提示正在註冊...而後會發送一個通知,沒看到這個是什麼意思 [signUpSignal subscribeNext:^(id x) {

 

[self.commitButton.rac_command.executionSignals subscribeNext:^(RACSignal *signUpSignal) {
        @strongify(self)
        [self.view endEditing:YES];
        [SVProgressHUD showWithStatus:@"正在註冊..." maskType:SVProgressHUDMaskTypeClear];
        [signUpSignal subscribeNext:^(id x) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"MSFREQUESTCONTRACTSNOTIFACATION" object:nil];
            [SVProgressHUD dismiss];
        }];
    }];

收到錯誤時的信號

[self.commitButton.rac_command.errors subscribeNext:^(NSError *error) {
        [SVProgressHUD showErrorWithStatus:error.userInfo[NSLocalizedFailureReasonErrorKey]];
    }];
    

驗證碼倒計時Label

@property (nonatomic, weak) IBOutlet UILabel *counterLabel;

viewDidLoad中操做,viewModel上的對應的屬性變動時,會同步到這個控制器上的label來

    RAC(self, counterLabel.text) = RACObserve(self, viewModel.counter);
相關文章
相關標籤/搜索