IOS development: MVVM design patterns, +RAC response programming

A: why use MVVM?

Why use MVVM? Just because it won’t let me often forced meng.

Each project will be finished after his huge ViewController code was terrified, no matter what the network request, networking process, data, ViewController to jump interactive logic inside, even write your own code, also dare not open. I have to think about whether the MVC model is too backward. After all, it is called Massive View Controller. In fact, it is not reasonable to say MVC is backward, so it is more suitable to say that it is born in Taiyuan.

MVC history is very long, but in fact it is a kind of modular programming mode, whether it is MVVM or MVCS, or it is VIPER with hair standing on end, three modules of MVC standard to divide the subdivision down, make the function of each module is more independent and single, and the ultimate goal are in order to regulate the degree, improve code decoupling, and reduced maintenance costs. What specific mode according to the need of the project needs to decide, and here, I simply talk about their own understanding and design of the MVVM architecture, in my opinion.

Two: MVVM module division

The traditional MVC model is divided into: Model, View, Controller. Model is a data model, a weight of View, is responsible for the interface display, and Controller is responsible for the rest of the logic and business, instant Controller heart ten thousand horse rushing past.

MVVM mode is just one more ViewModel, it is the role of Controller burden, logic will be inside the Controller (mainly weak business logic) transfer to itself, actually it relates to work more than these, also include the page display data processing etc.. (in the later chapters, there will be specific explanations.)

IOS development: MVVM design patterns, +RAC response programming

My design is like this:

  • A View corresponds to a ViewModel, and the View interface element property is bound to the data attribute after ViewModel processing
  • Model only needs to be created when there is network data, its role is only a secondary station of the data, that is, a very brief introduction of thin model
  • This weakens the role of Model, the network data processing logic in ViewModel, that is to say, only in the network data show View ViewModel, will see the shadow of Model, and the processed data will become the ViewModel attribute, note that these properties must as far as possible “intuitive”, for example, can be written in the UIImage is not written in URL
  • ViewModel and Model can see if attributes binding is needed
  • The role of Controller is the main View by initializing the ViewModel with the corresponding, and then added to the self.view, and then monitor the jump logic trigger and a small part of the business logic, of course, ViewController still need to jump here. Note: the bindings mentioned here are actually listening to attributes. When attributes change, listeners do some logical processing, and a powerful framework — RAC

Three: ReactiveCocoa

RAC is a powerful tool, and its use in conjunction with the MVVM model can only be described in one word – perfect.

Of course, some developers are reluctant to use these things, probably because they think it destroys the perception of complex logic agents, notification, monitoring and block etc., but I strongly RAC here, because my MVVM build ideas it will involve a large number of events, property transfer, I don’t want to write ten thousand a simple protocol to achieve these functions, the use of RAC can greatly simplify the code, make the logic more clear.

I will be on my MVVM framework for the realization of ideas to do a detailed explanation, before this, if you have not used RAC, please move:

IOS development: MVVM design patterns, +RAC response programming

roughly about RAC after the can down (~^~)

Four: MVVM module specific implementation

This is the interface to implement:

IOS development: MVVM design patterns, +RAC response programming

1, Model

Here I weaken the role of Model, it is only as a network request data transfer station, only when View needs to display network data, the corresponding ViewModel only have Model related processing.

2, ViewModel

In actual development, a View corresponds to a ViewModel, corresponding to the primary View and bound to a master ViewModel.

The main ViewModel bear network request, click event protocol, ViewModel and initialization sub attributes to the initial value of ViewModel network; the request data returned successfully after the main ViewModel ViewModel attribute also need to assign a new value.

The perception of the master ViewModel is like this:

#import < Foundation/Foundation.h> #import "MineHeaderViewModel.h" #import "MineTopCollectionViewCellViewModel.h" #import "MineDownCollectionViewCellViewModel.h" @interface MineViewModel: NSObject //viewModel @property (nonatomic, strong) MineHeaderViewModel *mineHeaderViewModel (nonatomic, strong); @property NSArray< MineTopCollectionViewCellViewModel *> *dataSorceOfMineTopCollectionViewCell; @property (nonatomic, strong) NSArray< MineDownCollectionViewCellViewModel *> *dataSorceOfMineDownCollectionViewCell; @property (nonatomic, strong) NSArray< MineDownCollectionViewCellViewModel; *> *dataSorceOfMineDownCollectionViewCellOther; //RACCommand @property (nonatomic, strong) RACCommand *autoLoginCommand; //RACSubject @property (nonatomic, strong) RACSubject *pushSubject; @end

Among them, RACCommand is the network request, equivalent to the RACSubject agreement, here for the click event agent, and ViewModel following a ViewModel property and three with an array of ViewModel I need to say.

In iOS development, we usually custom View, and custom View may be inherited from the UICollectionviewCell (UITableViewCell, UITableViewHeaderFooterView), when we define a View, the View does not need to reuse and only one, we will in the main ViewModel statement a ViewModel attribute, when we define a need to reuse cell, item, headerView and so on, we will in the main ViewModel statement for the storage array properties, cell, item multiplexing ViewModel, central idea is still a View corresponding to a ViewModel.

In the.M file, you do lazy loading on these properties, and configure RACCommand and RACSubject to make it convenient and then trigger and invoke when necessary. The code is as follows:

#import "MineViewModel.h" #import "LoginBackInfoModel.h" #import "UIImage+YB.h" #import "AutoLoginAPIManager.h" @implementation MineViewModel (instancetype init) {self = [super init]; if (self) {[self initialize]}; return self;} - {(void) initialize [self.autoLoginCommand.executionSignals.switchToLatest subscribeNext:^ (ID responds) {if (responds! Else) {if} ([responds isKindOfClass:[NSString class]]) {[MBProgressHUD showHintAlertWithText: (NSString *) responds view:[UIApplication sharedApplication].keyWindow];} else {NSDictionary = *resultDic (NSDictionary *) responds; if ([resultDic[@ "status" isEqualToString:@ "error"]) { [MBProgressHUD showHintAlertWithText:resultDic[@ "error_desc view:[UIApplication sharedApplication].keyWindow]"];} else {/ / [[LoginBackInfoModel shareLoginBackInfoModel] mj_setKeyValues:resultDic[@ dictionary model "auto_login";}}}]; [[[self.autoLoginCommand.executing skip:1] take:1] subscribeNext:^ (ID x) {if ([x isEqualToNumber:@ (1)])} {#pragma}}]; mark * * * getter * * * - (RACSubject * pushSubject) {if (_pushSubject! = [RACSubject) {_pushSubject subject]}; return _pushSubject;} - (RACCommand * autoLoginCommand) {if (! _autoLoginCommand) {_autoLoginCommand = [[RACCommand alloc] initWithSignalBl Ock:^RACSignal * (ID input) {return [RACSignal createSignal:^RACDisposable * (id< RACSubscriber> subscriber) {if ([[NSUserDefaults! StandardUserDefaults] objectForKey:@ "token"]) {[subscriber sendCompleted]; return nil;} NSDictionary *paramDic = "act" @{@: @ @ "auto_login", "token" [[NSUserDefaults standardUserDefaults] objectForKey:@ "token"]}; [[AutoLoginAPIManager new] startAPIManagerWithParagmas:paramDic success:^ (ID datas) {[subscriber sendNext:datas]; [subscriber sendCompleted];} failure:^ {[subscriber sendNext:error (NSString *errorMsg) Msg]; [subscriber sendCompleted];}]; return nil;}]}]; return _autoLoginCommand;};} - (MineHeaderViewModel * mineHeaderViewModel) {if (! _mineHeaderViewModel) {_mineHeaderViewModel = [MineHeaderViewModel new]; _mineHeaderViewModel.headerBackgroundImage [UIImage imageNamed:@ = "BG"]; _mineHeaderViewModel.headerImageUrlStr = nil; [[[RACObserve ([LoginBackInfoModel shareLoginBackInfoModel], headimg distinctUntilChanged] takeUntil: self.rac_willDeallocSignal] subscribeNext:^ (ID) x) {if (x = = Nil) {_mineHeaderViewModel.headerImageUrlStr} else {_mineHeaderV = nil; IewModel.headerImageUrlStr = x;}}]; [[[RACObserve ([LoginBackInfoModel shareLoginBackInfoModel] user_mobile_mask) distinctUntilChanged] takeUntil:self.rac_willDeallocSignal] subscribeNext:^ (ID x) {if (x = = Nil) { = @ {else} "; = x;}}]; [[[RACObserve ([LoginBackInfoModel shareLoginBackInfoModel] nickname) distinctUntilChanged] takeUntil:self.rac_willDeallocSignal] subscribeNext:^ (ID x) {if (x = = nil) { = @" click login ";} else { = x;}}]; Return _mineHeaderViewModel}} - (NSArray< MineTopCollectionViewCellViewModel; *> * dataSorceOfMineTopCollectionViewCell) {if (! _dataSorceOfMineTopCollectionViewCell) {MineTopCollectionViewCellViewModel *model1 = [MineTopCollectionViewCellViewModel new] [UIImage imageNamed:@; model1.headerImage = "wdqb_ye"]; model1.headerTitle = @ "balance"; [[[RACObserve ([LoginBackInfoModel shareLoginBackInfoModel] balance) distinctUntilChanged] takeUntil: self.rac_willDeallocSignal] subscribeNext:^ (ID x) {if (x = = Nil) {model1.content = @ {else} "; model1.content = x;}}]; MineTopCollectionViewCellViewModel *model2 [MineTopCollectionViewCellViewModel = new]; model2.headerImage = [UIImage imageNamed:@ model2.headerTitle = "redPacket"]; @ "red envelopes"; [[[RACObserve ([LoginBackInfoModel shareLoginBackInfoModel] coupon_num) distinctUntilChanged] takeUntil:self.rac_willDeallocSignal] subscribeNext:^ (ID x) {if (x = = Nil) {model2.content = @ {else} "; model2.content = x; _dataSorceOfMineTopCollectionViewCell = @[model1,}]}; model2] return _dataSorceOfMineTopCollectionViewCell;};} - (NSArray< MineDownCollectionViewCellViewModel *> dataSorceOfMineDownCollectionViewCell if (*) {_dataSorceOfMineDownCollectionViewCell {!) NSMutableArray *modelArr = mutableCopy] [@[]; NSArray *imageNamesArr = "wodi_qb" @[@ @ @ "wodi_dianhua", "wodi_lishi-1"]; NSArray *names = @[@ "my wallet" @ "contact customer service", @ "I want cooperation"]; for (int i = 0; I < imageNamesArr.count; I + +) {MineDownCollectionViewCellViewModel *model = [MineDownCollectionViewCellViewModel new]; model.headerImage [UIImage = imageNamed:imageNamesArr[i]]; model.title = names[i]; [modelArr = addObject:model];} _dataSorceOfMineDownCollectionViewCell [NSArray arrayWithArray:modelArr] return _dataSorceOfMineDownCollectionViewCell;}} - (NSArray< MineDownCollectionViewCellViewModel; *> * dataSorceOfMineDownColl) EctionViewCellOther {if (! _dataSorceOfMineDownCollectionViewCellOther) {NSMutableArray *modelArr = mutableCopy] NSArray *imageNamesArr = @[@ [@[]; "xx_1", "wyfk"]; @ NSArray *names @ = @[@ "help", "I want feedback"]; for (int i = 0; I < imageNamesArr.count I; *model [MineDownCollectionViewCellViewModel = {MineDownCollectionViewCellViewModel + +) new] model.headerImage; imageNamed:imageNamesArr[i]] = [UIImage; model.title = names[i]; [modelArr addObject: model]; _dataSorceOfMineDownCollectionViewCellOther = [NSArray arrayWithArray:modelArr]}}}; return _dataSorceOfMineDownCollectionViewCellOther; @end

For convenience, I will write some code directly before the paste up, don’t be frightened by its length, you can completely ignore the internal implementation, only need to know, but here is the realization of a RACCommand and RACSubject and ViewModel initialization.

Yes, the main work of the master ViewModel is basically only these three.

About the property binding logic, I’ll talk about it later.

Let’s take a look at the look of the sub ViweModel first:

#import < Foundation/Foundation.h> @interface; MineTopCollectionViewCellViewModel: NSObject @property (nonatomic, strong) UIImage *headerImage; @property (nonatomic, copy) NSString *headerTitle @property (nonatomic, copy); NSString *content; @end

I didn’t post the code inside.M because there was no code in it (Hei Hei).

Next, why do I design the sub ViewModel with only a few properties, while the main ViewModel has so much logic?.

First of all, let’s take a look at the concept of ViewModel. Model is the model, so ViewModel is the model of view. While in the traditional MVC, thin Model called data model, in fact, thin Model called DataModel is more appropriate; and only fat Model network requests, logical network data processing logic is written on the inside, easy to display data in View more convenient so fat Model function and ViewModel similar, I put it called the “little band of ViewModel”.

With that in mind, we seem to be putting the logic of network data processing in the sub ViewModel to alleviate the burden on the main ViewModel.

I would like to do that.

But there’s a problem. Let’s give you a simple example, such as this demand:

IOS development: MVVM design patterns, +RAC response programming

The general idea is to customize a CollectionviewCell and a ViewModel, because their layout is the same, we need to declare an array of attributes in the main ViewModel, then add two ViewModel, corresponding to two Cell.

Static data such as image and title can be assigned to the two sub ViewModel in the primary ViewModel, while the exact amount and amount of data below are derived from the network, and the data requested by the network is usually:

{balance: "100", redPacket: "3"}

We need to translate “100” into “100 yuan” and “3” into “3””. The
network data processing logic according to normal logic should be placed in the ViewModel, but there is a problem, our collectionviewcell is multiplexed, its ViewModel is the same, but the data processing is two different fields, we have to distinguish? And don’t forget that the successful data obtained by the network request is in the primary ViewModel and also involves the value passing. Again according to this train of thought to realize, necessarily more complex, so I simply across the board, whether it is static data or network data processing, all put in the main ViewModel.

In doing so, although the main ViewModel task is heavy, the sub ViewModel is too lightweight, but the benefits are many, one by one enumeration:

  • In the lazy loading of the main ViewModel, the initialization of the sub ViewModel and the initial value are achieved. After the network request succeeds in RACCommand, the primary ViewModel needs to assign the sub ViewModel again. The assignment is clear, with two modules.
  • The data attribute ViewModel only put the corresponding View, equivalent to Model, but is more flexible than Model, because if the View has some click events, we can also add RACSubject in ViewModel (or agreement), ViewModel high flexibility.
  • Whether it is static data or network unified data processing, all the sub ViewModel initialization and attribute assignment together, all network requests together, all RACSubject together, clear structure, convenient maintenance.

3, View

As mentioned earlier, the only scenario in which ViewModel interacts with Model is the situation where network request data needs to be displayed, while View and ViewModel are one-to-one, and binding does not depend on the situation. Details are described below.

Custom View is handled here in two separate cases:

(1) View that does not inherit a multiplex mechanism (not inheriting UICollectionviewCell, etc.)

Here, take the main View of the interface as an example


- - (instancetype) initWithViewModel: (MineViewModel *) viewModel;

The View needs to bind to the ViewModel, implement the logical and trigger events, and guarantee the uniqueness of the ViewModel.


No code is posted here, and the interaction between View and ViewModel is nothing more than triggering a network request, triggering a click event, and displaying ViewModel data properties on the interface. If you’re going to have some RAC, of course, this is a piece of cake, but if you stick to Apple’s native protocol and notification, it will be a bit of a hassle to implement.

(2) inherit View (UICollectionviewCell, etc.) with multiplexing mechanism

The most noticeable is the reuse mechanism of cell and item.

When we customize these cell and item, and can not bind the corresponding ViewModel, because it will appear the multiplexing principle, multiple cell (item) ViewModel as like as two peas here, I choose, I think a better solution.

First, declare a ViewModel attribute in the custom cell (item).H.

#import < UIKit/UIKit.h> #import "MineTopCollectionViewCellViewModel.h" @interface MineTopCollectionViewCell: UICollectionViewCell @property (nonatomic, strong) MineTopCollectionViewCellViewModel *viewModel; @end

Then, in the setter method of the property, assign the interface element of the cell:

#pragma mark * * * setter * * * - (void) setViewModel: (MineTopCollectionViewCellViewModel * viewModel) {if (! ViewModel) {return}; _viewModel = viewModel; RAC (self, contentLabel.text) = [[RACObserve (viewModel, content) distinctUntilChanged] takeUntil:self.rac_willDeallocSignal]; self.headerImageView.image = viewModel.headerImage; self.headerLabel.text = viewModel.headerTitle;}

Ps: here to see RAC again (RACObserve) and (two) this is a macro, this property, if you do not understand, can not control, behind I will explain the property of my ideas, including the use of ReactiveCocoa to achieve the same effect (which is completely dead!!!).

The role of rewriting setter, you should know, is written in the protocol method of collection view:

Cell.viewModel = self.viewModel.collectionCellViewModel;

When you can execute into the setter method, change the layout of the cell.

Well, that’s the essence, nonsense.

Think about it, or stick to the main View’s.M code (again, focus on ideas):

#import "MineView.h" #import "MineHeaderCollectionReusableView.h" #import "MineTopCollectionViewCell.h" #import "MineDownCollectionViewCell.h" @interface (MineView) < UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> @property (nonatomic, strong) UICollectionView *collectionView @property (nonatomic, strong); MineViewModel *viewModel; @end @implementation MineView (instancetype) initWithViewModel: (MineViewModel * viewModel) {self = [super init] if; (self) [UIColor colorWithRed:243/255.0 green:244/255.0 blue:245/255.0 {self.backgroundColor = alpha:1]; self.viewModel = viewModel; [self addSubview:self.collectionView]; [self setNeedsUpdateConstraint S]; [self updateConstraintsIfNeeded]; [self bindViewModel];} return self;} - {(void) updateConstraints __weak typeof (self) weakSelf = self; [self.collectionView mas_makeConstraints:^ (MASConstraintMaker *make) {make.edges.mas_equalTo (weakSelf);}]; [super updateConstraints];} - (void bindViewModel) {[self.viewModel.autoLoginCommand execute:nil];} #pragma mark UICollectionViewDataSource (NSInteger - * * * * * * (numberOfSectionsInCollectionView:) UICollectionView * collectionView) {return 4;} - (NSInteger) collectionView: (UICollectionView *) collectionView numberOfItemsInSection: (NSInteger) section (section) {switch {case} 0: {return 0; Break; case 1: {return self.viewModel.dataSorceOfMineTopCollectionViewCell.count;} case {return break; 2: self.viewModel.dataSorceOfMineDownCollectionViewCell.count break;}; case 3: {return self.viewModel.dataSorceOfMineDownCollectionViewCellOther.count}; break default:; return 0; break;}} - (UICollectionViewCell *) collectionView: (* UICollectionView) collectionView cellForItemAtIndexPath: (NSIndexPath * indexPath) {switch (indexPath.section) {case {return 0: nil break case 1:;}; *cell dequeueReusableCellWithReuseIdentifier:[NSString stringWithUTF8String:object_getClassName {MineTopCollectionViewCell = [collectionView ([MineTopCollectionViewCell class])] forIndexPath:indexPath]; cell.viewModel = self.viewModel.dataSorceOfMineTopCollectionViewCell[indexPath.row]; return cell;} break {MineDownCollectionViewCell = case; 2: *cell [collectionView dequeueReusableCellWithReuseIdentifier:[NSString stringWithUTF8String:object_getClassName (MineDownCollectionViewCell [class]] forIndexPath:indexPath]); cell.viewModel = self.viewModel.dataSorceOfMineDownCollectionViewCell[indexPath.row]; return cell;} Break; case 3: {MineDownCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[NSString stringWithUTF8String:object_getClassName ([MineDownCollectionViewCell class])] forIndexPath:indexPath]; cell.viewModel = self.viewModel.dataSorceOfMineDownCollectionViewCellOther[indexPath.row]; return cell;} break default: return; nil; break;}} - (UICollectionReusableView *) collectionView: (* UICollectionView) collectionView viewForSupplementaryElementOfKind: (NSString * kind) atIndexPath: (NSIndexPath * indexPath) {if (kind = = UICollectionElementKindSectionHeader switch (indexPath.section)) {{ Case 0: MineHeaderCollectionReusableView [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:[NSString {*view = stringWithUTF8String:object_getClassName ([MineHeaderCollectionReusableView class)] forIndexPath:indexPath]; view.viewModel = self.viewModel.mineHeaderViewModel; return view; break default: break;};}}} return nil; #pragma mark * * * UICollectionViewDelegate * * * - (void) collectionView: (UICollectionView * collectionView) didSelectItemAtIndexPath: (NSIndexPath * indexPath) {[self.viewModel.pushSubject sendNext:nil]}; #pragma Ma Rk * * * UICollectionViewDelegateFlowLayout * * * - (CGSize) collectionView: (UICollectionView * collectionView) layout: (UICollectionViewLayout * collectionViewLayout) sizeForItemAtIndexPath: (NSIndexPath * indexPath) {switch (indexPath.section) {case} 0: {return CGSizeZero; break; case 1: {return CGSizeMake (CGRectGetWidth (self.bounds) / 2 - 0.5, 80}); break; case 2: {return CGSizeMake (CGRectGetWidth (self.bounds), 44);} break {return (CGSizeMake; case 3: CGRectGetWidth (self.bounds), 44);} break default:; return CGSizeZero; Break;}} - (CGSize) collectionView: (UICollectionView * collectionView) layout: (UICollectionViewLayout *) collectionViewLayout referenceSizeForHeaderInSection: (NSInteger section) {if (section = = 0) {return CGSizeMake (CGRectGetWidth (self.bounds), 150 return CGSizeZero);};} - (CGFloat) collectionView: (UICollectionView *) collectionView layout: (UICollectionViewLayout*) collectionViewLayout minimumLineSpacingForSectionAtIndex: (NSInteger) section (section) {switch {case} 0: {return 0; break; case 1: {return 0}; break case; 2: return {1}; break case; 3: {return 1}; break default:; return 0; break;}} - (CGFloat) collectionView: (UICollectionView *) collectionView layout: (UICollectionViewLayout*) collectionViewLayout minimumInteritemSpacingForSectionAtIndex: (NSInteger) section (section) {switch {case} 0: {return 0; break; case 1: {return 1;} break {return 0; case 2: break case 3:;};} break {return 0; return 0; default:; break;}} - (UIEdgeInsets) collectionView: (UICollectionVie W collectionView layout: (UICollectionViewLayout) * *) collectionViewLayout insetForSectionAtIndex: (NSInteger) section (section) {switch {case} 0: {return UIEdgeInsetsZero; break; case 1: {return UIEdgeInsetsMake (0, 0, 0, 0);} case {return break; 2: UIEdgeInsetsMake (10, 0, 0, 0);} break; case 3: {return UIEdgeInsetsMake (10, 0, 0, 0}); break; default: return UIEdgeInsetsZero; break #pragma mark;}} - * * * * * * Getter (UICollectionView * collectionView) {if {_collectionView (_collectionView!) [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:[UICollectionViewFlowLayout = new]]; _collectionView.delegate = self; _collectionView.dataSource = self; _collectionView.alwaysBounceVertical = YES; _collectionView.backgroundColor = [UIColor clearColor]; [_collectionView registerClass:[MineTopCollectionViewCell class] forCellWithReuseIdentifier:[NSString stringWithUTF8String:object_getClassName ([MineTopCollectionViewCell class]) [_collectionView registerClass:[MineDownCollectionViewCell class] forCellWithReuseIdentifier:[NSString "; stringWithUTF8String:object_getClassName ([MineDownCollectionViewCell class])"; [_collectionView registerClass:[MineHeaderCollecti OnReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:[NSString stringWithUTF8String:object_getClassName ([MineHeaderCollectionReusableView class] return _collectionView ");};} - (MineViewModel * viewModel) {if (_viewModel! = [[MineViewModel) {_viewModel alloc] init]}}; return _viewModel; @end

4, Controller

This guy’s liberated.

#import "MineViewController.h" #import "MineView.h" #import "MineViewModel.h" #import "LoginViewController.h" @interface (MineViewController) @property (nonatomic, strong) MineView *mineView @property (nonatomic, strong); MineViewModel *mineViewModel; @end @implementation MineViewController #pragma mark * * * life * * * cycle - (void) viewDidLoad {[super viewDidLoad]; self.hidesBottomBarWhenPushed = YES; [self.view / addSubview:self.mineView]; AutoLoginAPIManager new]; [self bindViewModel];} - {(void) updateViewConstraints __weak typeof (self) weakSelf = self; [self.mineView mas_makeConstraints:^ (MASConstraintMaker *make) {make.edges.mas_equalTo (weakSelf.view);}]; [super updateViewConstr Aints] - (void);} bindViewModel {@weakify (self); [[self.mineViewModel.pushSubject takeUntil:self.rac_willDeallocSignal] subscribeNext:^ (NSString *x) {@strongify (self); [self.navigationController pushViewController:[LoginViewController new] animated:YES]}]; #pragma mark;} - * * * * * * getter (MineView * mineView) {if (_mineView! = [[MineView) {_mineView} alloc] initWithViewModel:self.mineViewModel]; return _mineView; (MineViewModel * mineViewModel)} - {if (_mineViewModel! = [[MineViewModel) {_mineViewModel alloc] init]}}; return _mineViewModel; @end

Is not very clear, and even doubt its existence sense (~_~).

Five: additional narrative

1, binding ideas

I want to know some RAC people know property binding, RAC (,) and RACObserve (,), which is the most commonly used, it is the role of the a attribute is bound to B class’s B property, a property when a change occurs, the B property of the B class will automatically make the appropriate treatment of.

This can solve quite a lot of needs, such as: user information display interface -> login interface -> login successful -> back to the user information display interface -> display user information

In the past, our practice was usually that the user information display interface wrote a notification to listen to -> login successfully sent notification -> the user information display interface refreshed the layout

Of course, can also use the block protocol, what, this seemingly did not see how complicated, but once the code amount after You’ll see. what called Meng force, while property binding, using RAC combination of attributes and a series of methods, will have a multiplier effect, fully reduce the coupling the degree of code, reduce the maintenance cost, thinking more clearly.

This needs to be done in the above requirements:

Show user information about attributes of View, such as,, etc., and data binding in the corresponding ViewModel. In the main ViewModel, the initialization and assignment of the child ViewModel, the user information shows the content of the View, is the initial value. When the main ViewModel network request succeeds, the sub ViewModel is assigned again, and the user information display interface displays the corresponding data.

You don’t have to do anything without pollution.

Also, we can do better, just like I do (the above code may be a bit messy, feel shy), will display the contents of ViewModel and View binding properties of the attributes of the binding properties and Model ViewModel, see a figure:

IOS development: MVVM design patterns, +RAC response programming
, here’s the picture description

As long as the Model property is changed, passed to the View to make the interface elements change automatically without adding. With this thing later, reloadData this method may be less.

2, the overall logic comb

  1. Entering ViewController, lazy loading initializes the primary View (calling the -initWithViewMdoel method, ensuring the primary ViewModel uniqueness), lazy loading, and initializing the primary ViewModel.
  2. Enter the main ViewModel, initialize the configuration network request, click logic, initialize the sub ViewModel.
  3. Enter the main View, initialize through the master ViewModel, call the corresponding logic in the ViewModel and the corresponding sub ViewModel to display the data.
  4. The interaction between ViewController and ViewModel is mainly jump logic and so on.

3, create your own architecture

In fact, in any project, if a module code is too big, we can be our own code separation, just follow certain rules (of course, this is the definition of their own rules), the ultimate goal is to make the business function and refinement, classification.

This is equivalent to a handful of sand on the beach, in the beginning we separate the stones and sand, but later found that the sand also has a small, so we again according to the size of the sand is divided into two parts, then found that the color of the sand is too much, we have to separate the colors of sand……

In MVVM mode, can put ViewModel network request logic is proposed, called NetworkingCenter; can also put out in the ViewModel click on the various listening event called ActionCenter; can also use a variety of configuration interface display View (such as tableView protocol method in data written) proposed, called UserInterfaceConfigDataCenter; if you need to handle project network requests a lot of data, we can divide the data processing logic is proposed, called DataPrecessCenter……

Remember a word: change from the pope.

Six: conclusion

The mobile terminal architecture is always no universal structure, the myriads of changes, only the universal programmer, select the appropriate structure according to the demand of products is the correct practice, MVC is old, but in small projects is still practical; the MVVM+RAC is powerful, but sometimes will increase the amount of code, but MVVM and Android. The MVP model has quite a bit in common, can be used as MVCS did not understand; what can be said, VIPER model looks more powerful, think may be to refine the guess which modules, ViewModel? Hey, I didn’t study VIPER, do not display slight skill before an expert.

If you are free, will continue to update some of the needs of the article (~_~)