preface

I have been engaged in front-line business development work and contacted the business line every day. After a long time, I began to think about how to optimize the architecture and improve the maintenance efficiency, which led to my contact with MVVM.

The emergence of MVVM is mainly to solve the problem that the Controller becomes increasingly large in the development process and becomes difficult to maintain. Therefore, MVVM frees the task of data processing from the Controller and makes the Controller only need to focus on specific business work. The ViewModel takes care of the data processing and uses various notification mechanisms to make the View respond to changes to the ViewModel.

There is also a problem that is easy to ignore. When most domestic and foreign materials describe MVVM, they are arranged like this: model-view-viewModel, which objectively creates the illusion that MVVM does not need Controller, but is this the case in fact?Copy the code

Analysis usage logic

The ViewModel part

When I first heard about MVVM, I was confused, because I did not see the shadow of Controller among these children, and I knew nothing about the logic of Controller. But my gut tells me that the Controller is still not available, at least when it comes to manipulating views. The word ViewModel itself bothers me. The term itself can lead to confusion because it’s a mash-up of two terms we already know which side of the structure is View versus Model.

When I look up the information, most of the expression of MVVM on the Internet is View <-> ViewModel <-> Model, are saying that ViewModel supports two-way binding of data, vague expression, I still can not fully understand the real use of ViewModel and the meaning of existence.

Like the following:

It turns the Model information into the information required by the View, and also passes commands from the View to the Model. View and Model are bidirectional bound through ViewModel.

I focused on the first sentence above, “It converts model information to View information and also passes commands from View to Model.” When I further understand the role of ViewModel, I found that ViewModel should be called as “the observation coordinator between View and Model”. Its main function is to get the original data, process it according to the needs of specific business logic, and then plug the processed things into the View. One of its responsibilities is the static Model, which means that the View displays the data it needs. This gives the View a more clearly defined task, that is, to present the data provided by the View Model. In a word, the Model directly corresponds to the View and logically belongs to the Model layer.

The Controller part

So what does a Controller do?

MVVM is actually based on the architectural idea of the fat Model, and then split into two parts of the fat Model: Model and ViewModel. MVC -> fat Model -> Model+ ViewModel.

As with the previous analysis, the ViewModel is essentially a Model layer (as the Model that corresponds directly to the View itself), so it is not appropriate for a View to hold the ViewModel directly, and vice versa. Because the ViewModel may not necessarily serve a particular View, a looser binding reduces the coupling between the ViewModel and View. Now, the Controller’s only concern is to configure and manage various views using data from the ViewModel. The Controller’s role is to control the binding and calling relationship between the View and the ViewModel. The Controller doesn’t need to know about Web service calls. Core Data, Model objects, things like that, you can throw them into the ViewModel.

The following two images may give you a more intuitive feeling:

Photo: www.sprynthesis.com/2014/12/06/…

Let me explain what the picture refers to:

  1. The View in MVVM essentially refers to the View and Controller two-part structure.
  2. The ViewModel in MVVM consists of a ViewModel separated from some operations of the Controller, while the workload of the Controller is greatly reduced.
  3. When the Controller is separated from the ViewModel, the View in MVVM can also be divided into View and Controller, which becomes the structure of the second picture MVCVM, and the agent distribution of Controller becomes smaller.

Therefore, in the final analysis, MVVM should be described as View <-> C <-> ViewModel <-> Model, that is, the design mode of MVCVM, which clearly explains the position and role of Controller.

Further combing

From the above, we can logically conclude that the Controller should hold the ViewModel and View, and judge and match them. A ViewModel may not necessarily serve a particular View, but a Controller may have many views, so the same Controller may hold many viewModels to implement different business logic to control multiple views.

The Controller’s sole concern is to configure and manage various views using data from the ViewModel, and to let the ViewModel know when relevant user input needs to change upstream data occurs. The Controller doesn’t need to know about network requests, database operations, models, etc., so the Controller is more focused on handling specific business logic.

Photo: www.sprynthesis.com/2014/12/06/…

  • Controller: Is responsible for the deployment and binding relationship between View and ViewModel and the execution of business logic.
  • View: Displays the UI and accepts the Action.
  • ViewModel: Network request, data processing, data holding.
  • Model: Raw data.

The following call relationship is roughly formed:

Code demo

Nonsense not much to say, first Demo: github.com/derekhuangx…

I’m going to take some of the important code and show you the logic

MarshalModel

MarshalModel.h
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger lessonCount;
Copy the code

The Model is relatively simple, storing raw data.


MarshalViewModel

MarshalViewModel.h @interface MarshalViewModel : NSObject @property (nonatomic, strong, readonly) NSString *name; @property (nonatomic, strong, readonly) NSString *detailTextString; @property (nonatomic, assign, readonly) UITableViewCellAccessoryType cellType; - (instancetype)initWithModel:(MarshalModel *)model; @end MarshalViewModel.m @interface MarshalViewModel () @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSString *detailTextString; @property (nonatomic, assign) UITableViewCellAccessoryType cellType; @end@implementation MarshalViewModel - (instanceType)initWithModel (MarshalModel *)model; { if (self = [super init]) { self.name = model.name; If (model.lessonCount > 35) {self.detailTextString = @detailTextString; self.cellType = UITableViewCellAccessoryDetailDisclosureButton; } else {self.detailTextString = [NSString stringWithFormat:@"课程数 :%ld", (long) model.lessoncount]; self.cellType = UITableViewCellAccessoryNone; } } return self; }Copy the code
  • The.h file of the ViewModel only places the initialization code, which performs the injection of Model data, and provides the interface exposed to the ViewreadonlyOf course, you can also use this identifierGETMethod instead, this will prevent the View from modifying the data in the ViewModelData is injected from one line, processed, and taken out from the other.
  • The.m file of the ViewModel, so to speak, is the processing of the raw data, and this is basically the processing of the raw data needed to convert it into the View. Of course, you can also add Cache or database operations according to their own needs, and you can also add network data access operations.

MarshalCell

MarshalCell.h
@interface MarshalCell : UITableViewCell
@property (nonatomic, strong) MarshalViewModel *viewModel;
@end

MarshalCell.m
- (void)setViewModel:(MarshalViewModel *)viewModel {
	_viewModel = viewModel;
    self.textLabel.text = viewModel.name;
    self.detailTextLabel.text = viewModel.detailTextString;
    self.accessoryType = viewModel.cellType;
}
Copy the code

I’m just showing you the SET method in the dot m file, and I think that makes sense, and the ViewModel provides all the key elements that a View needs to handle properly, so the View becomes very flat, and you just have to handle Action events like clicks and pass them to the Controller, You don’t need to focus on the actual business logic of the raw data transformation.


HomeViewController

HomeViewController.m

@interface HomeViewController () 
@property (nonatomic, strong) NSMutableArray <MarshalViewModel *>*viewModels;
@end

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

	MarshalCell *cell = [[MarshalCell alloc] initWithStyle: UITableViewCellStyleSubtitle
					       reuseIdentifier: kMarshalCellIdentifier];
	cell.viewModel = self.viewModels[indexPath.row];
	return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	return self.viewModels.count;
}

- (void)getData {
	[[MarshalService shareInstance]fetchDataCompletion:^(NSArray<MarshalModel *> * _Nonnull data) {
    	self.viewModels = [self viewModelsWithDatas:[data mutableCopy]];
    	dispatch_async(dispatch_get_main_queue(), ^{
    		[self.tableView reloadData];
		});
	} failure:^(NSError * _Nonnull error) {
		NSLog(@"Failed to fetch courses:%@", error);
	}];
}
Copy the code

As you can see, I used an array to store the ViewModel. The reason is that the UI display is inconsistent because the data corresponding to each Cell is inconsistent, so each Cell needs to have its own ViewModel to fill its UI data. This is further proof that the ViewModel is still essentially the Model layer.

Question Points (irregularly updated)

1. Why does a ViewModel not have.h interface readable processing?

In the ViewModel, data is injected one-way, and then data is processed according to the business logic, and then data interface is provided to the View. In order to ensure that the data will not be polluted, so the identifier of 'readonly' is added, while the View does not need the above logic.Copy the code

2. How to send the value after clicking a part of a Cell?

1. The Controller finds the corresponding ViewModel. The ViewModel acts as the Mother ViewModel and replaces the Controller to do the dirty work of generating the corresponding Child ViewModel. 2.ViewModel Returns Child ViewModel to Controller. 3.Controller Uses Child ViewModel to generate the Controller on the next page and perform the jump operation.Copy the code

Refrence

  • ReactiveCocoa and MVVM, an Introduction
  • IOS application architecture on view layer organization and call scheme

In addition

  • Jane’s address book
  • Denver address