MVVM+RAC write a login demo

Structure of MVVM

We will extract the logic module MVC Controller to the ViewModel, so that the Controller is only responsible for the interface display, where Controller by binding and ViewModel data notification:

Model layer, API request the original data
layer, the view is displayed by the viewController to control the
viewModel layer, responsible for business processing and data conversion

MVVM features

MVVM easy to test the convenience of the business to facilitate the division of responsibilities, such as allowing the primary development and development of UI, advanced development and development of more elegant logic code, increase maintainability


In this paper, the use of ReactiveCocoa to complete the binding of this function, but before using it first need to understand some of his characteristics:

Study of the high cost of debugging requires higher skill which is a very heavy frame, almost replaced the official Apple mechanism of all events in each person’s code style are not the same, the use of the framework to team members almost every day with each other review code good team members there is of course: response type, high polymerization, low coupling

code analysis

Requirements: when the user input the phone number and the four bit verification code, the phone number rules, after the login request

Controller layer

We can see that the Controller layer is only a one-way bound the input box and the ViewModel character attributes, the code is very simple

#import "MRCLoginController.h" #import "MRCLoginViewModel.h" @interface (MRCLoginController) @property (weak, nonatomic) IBOutlet UITextField *mobileNumTF; @property (weak, nonatomic) IBOutlet UITextField *smsCodeTF (strong, nonatomic); @property MRCLoginViewModel *viewModel; @end @implementation MRCLoginController (void) viewDidLoad {[super viewDidLoad]; Do any additional setup after loading / the view from its nib. self.viewModel = [[MRCLoginViewModel alloc] init]; / / #pragma mark binding [self p_bindViewModel];} - bind - (void) p_bindViewModel{/ / telephone number RAC (self.viewModel, mobileNum) = self.mobileNumTF.rac_textSignal; / / verification code RAC (self.viewModel, smsCode) @end = self.smsCodeTF.rac_textSignal;}

ViewModel layer

  • 1 header file

Header file command here in the outside world and not with the use, but other viewModel may be used on it or take it out

#import "BaseViewModel.h" @interface MRCLoginViewModel: BaseViewModel @property (nonatomic, copy) NSString *mobileNum @property (nonatomic, copy); NSString *smsCode; @property (nonatomic, strong, readonly) RACCommand *loginCommand; @end
  • 2 implementation file

Here are a few holes to say:

RAC KVO writing can not observe the read-only attribute (in fact, OC can not) observe the object must be given the initial value, otherwise there may be a very strange question

#import "MRCLoginViewModel.h" #import "FYRequestTool.h" #import "SimulateIDFA.h" @interface (MRCLoginViewModel) @property (nonatomic, strong, readwrite) RACCommand *loginCommand; @end @implementation MRCLoginViewModel (instancetype) init{if (self = [super init]) {/ / this string must be initialized, otherwise the array will collapse! @ self.mobileNum = self.smsCode = "@"; ""; RACSignal * mobileNumSignal = [RACObserve (self, mobileNum) filter:^BOOL (NSString * value) {return value.length = = 11;}]; RACSignal * smsCodeSignal =[RACObserve (self, smsCode) filter:^BOOL (NSString * value) {return value.length = = 4; }]; @weakify (self); [[RACSignal combineLatest:@[mobileNumSignal, smsCodeSignal]] subscribeNext:^ (ID x) {@strongify (self); NSLog (@ "trigger request"); [self.loginCommand execute:nil]; return self}];}}; #pragma mark - get & & set (RACCommand * loginCommand{) if (NIL = = _loginCommand) {@weakify (self); _loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * (ID input) {@strongify (self); / / here and digit verification code in the controller have been screened here, just do a simple example, can check the regular mobile phone here The best way: / / button.rac_command = viewmodel.loginCommand... The number judgment over here if (self.mobileNum.length! = 11) {return [RACSignal error:[NSError errorWithDomain:@ "code:10" userInfo:@{@ "errorInfo": @ "mobile phone numbers do not to"}]];} if (self.smsCode.length! = 4) {return [RACSignal error:[NSError errorWithDomain:@ "code:20" userInfo:@{@ "errorInfo" @ "code number wrong"}]] return [self loginSignalWithMobileNum:self.mobileNum smsCode: self.smsCode];};}]}}; return _loginCommand; #pragma mark private (RACSignal * loginSignalWithMobileNum: * (NSString) MobileNo smsCode: (NSString) authCodeSMS{return [RACSignal createSignal:^RACDisposable * *) (id< RACSubscriber> subscriber) {/ / NSMutableDictionary *params add the deviceID parameter = [[NSMutableDictionary alloc]init]; [params setObject:[self deviceNo] forKey:@ "deviceNo"]; NSDictionary * dict = "mobileNo" mobileNo, @{@: @ "authCodeSMS": authCodeSMS}; NSMutableDictionary *newParams = [dict mutableCopy]; [newParams addEntriesFromDictionary:params]; / / network request [FYRequestTool POST:@ "parameters:newParams progress:nil success:^ (NSURLSessionDataTask * _Nonnull task ID, _Nullable responseObject) { Send content (what to success / what, also can be directly to the single assignment [subscriber sendNext:responseObject] [subscriber cases); sendCompleted];} failure:^ (NSURLSessionDataTask * _Nullable task NSError * _Nonnull error) {[subscriber sendError:error];}]; / / return [RACDisposable disposableWithBlock:^{to complete the signal after the cancellation of [FYRequestTool cancel];}];}];} - (NSString *) deviceNo [SimulateIDFA @end {return createSimulateIDFA];}