Talk about MVVM and chained network request architecture

Talk about MVVM and chained network request architecture

Preface

I have been studying the architecture of iOS for some time. Why?

The company's architecture has always been MVC. When we officially launched, the project already had over 100,000 lines of code. The main VCs generally have more than 2,000 lines of code.

The key is that we have only done one-third of the business logic in the current version...

So, restructure it.

text

MVVM

MVVM: Model-View-ViewModel

MVVM is actually an evolved version of MVC. It decouples business logic from VC to ViewModel to achieve a major "slimming" of VC.

Explain with code!

Make a simple login judgment:

Create LoginViewModel (logic processing), LoginModel (only data), and LoginViewController.

LoginView is not used here to allow beginners to better focus on decoupling with ViewModel.

Of course, if you understand all of this, you can directly look at Wzxhaha/RandomerFramework, which is the basic architecture (SubClasses+Protocol+MVVM+RAC) of the independent project Randomer that I am working on, as well as its login and registration module. In addition, thanks to Wang Longshuai for this article, which opened the door to a new world for me.

Add method to LoginModel

  1. //.h
  2.  
  3. - (instancetype)initWithUserName:(NSString *)username password :(NSString *) password ;
  4.  
  5.   
  6.  
  7. @property (nonatomic,copy,readonly)NSString * username;
  8.  
  9. @property (nonatomic,copy,readonly)NSString * password ;
  10.  
  11.  
  12. //.m
  13.  
  14. - (instancetype)initWithUserName:(NSString *)username password :(NSString *) password {
  15.  
  16. if (self = [super init]) {
  17.  
  18. _username = username;
  19.  
  20. _password = password ;
  21.  
  22. }
  23.  
  24. return self;
  25.  
  26. }

There is nothing much to say about this, just add an initialization method to the Model.

Add method to LoginViewModel

  1. #import "PersonModel.h"  
  2.  
  3.   
  4.  
  5. - (instancetype)initWithPerson:(PersonModel *)person;
  6.  
  7. @property (nonatomic,assign,readonly)BOOL canLogin;
  8.  
  9.  
  10. - (instancetype)initWithPerson:(PersonModel *)person {
  11.  
  12. if (self = [super init]) {
  13.  
  14. //Do the processing after you bind the model here
  15.  
  16. _canLogin = [self valiCanLoginWithUserName:person.username password :person.password ] ;
  17.  
  18. }
  19.  
  20. return self;
  21.  
  22. }
  23.  
  24.   
  25.  
  26. - (BOOL)valiCanLoginWithUserName:(NSString *)username password :(NSString *) password {
  27.  
  28. if (username.length & password .length) {
  29.  
  30. return YES;
  31.  
  32. } else {
  33.  
  34. return   NO ;
  35.  
  36. }
  37.  
  38. }

Add a binding model initialization method to ViewModel, as well as a method to determine whether the account password is valid.

Then VC (or View) can directly obtain the result of the judgment

  1. PersonModel * person = [[PersonModel alloc]initWithUserName:@ "10"   password :@ "10" ];
  2.  
  3. PersonViewModel * viewModel = [[PersonViewModel alloc]initWithPerson:person];
  4.  
  5.   
  6.  
  7. NSLog(@ "%d" ,viewModel.canLogin);

It doesn't matter when it comes to simple functions, but when you are dealing with complex logical judgments, MVVM will have huge advantages.

By the way, let me talk about ReactiveCocoa. The reason why I advocate MVVM so much is mainly because RAC and MVVM are a perfect match!

ReactiveCocoa

RAC has the characteristics of functional programming and responsive programming. If you are not familiar with programming ideas, you can read my WZXProgrammingIdeas

The main purpose of RAC is to monitor various events. RAC calls this a signal flow, and then receives the signal through block callback. A large number of blocks are used in it, so be sure to use @weakify(self) and @strongify(self) well.

Why do we say that RAC and MVVM are a perfect match?

MVVM decouples methods to ViewModel, but they still need to be called by VC (V), so the logic for determining when to call will still be complicated.

RAC solves this problem. It is responsible for monitoring events and then calling ViewModel to perform logical judgment.

For example:

  1. [[_registerBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
  2.  
  3. @strongify(self)
  4.  
  5. [self.viewModel toRegisterWithType:Register];
  6.  
  7. }];
  8.  
  9.  
  10.  
  11. [[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
  12.  
  13. @strongify(self)
  14.  
  15. [self.viewModel loginWithUserName:self.usernameTextField.text password :self.usernameTextField.text Success:^(idresponse) {
  16.  
  17. } failure:^{
  18.  
  19. SHOW_ERROR(@ "error" , @ "account or password error" )
  20.  
  21. } error:^(NSError *error) {
  22.  
  23. SHOW_ERROR(@ "error" , @ "network connection failed" )
  24.  
  25. }];
  26.  
  27. }];

RAC monitors the login and registration buttons, making the code concise and the structure very compact.

For a demo, check out this one: Wzxhaha/RandomerFramework

https://github.com/Wzxhaha/RandomerFramework

Or the simple version of WZXRACDemo

https://github.com/Wzxhaha/WZXRACDemo

Chained network request framework

Why encapsulate WZXNetworking

This is a framework with very scary fault tolerance.

  1. [[WZXNetworkManagermanager].setRequest(@ "http://192.168.1.40:8001" ).RequestType(POST).HTTPHeader(nil).Parameters(nil).RequestSerialize(RequestSerializerHTTP).ResponseSerialize(ResponseSerializerJSON) startRequestWithSuccess:^(id response) {
  2.  
  3.   
  4.  
  5. NSLog(@ "success" );
  6.  
  7. } failure:^{
  8.  
  9.   
  10.  
  11. NSLog(@ "failure" );
  12.  
  13. }];

Except for .setRequest(url) and startRequestWithSuccess failure methods, the others are not necessary.

You can do this:

  1. [[WZXNetworkManager manager].setRequest(@ "http://192.168.1.40:8001" ) startRequestWithSuccess:^(id response) {
  2.  
  3.   
  4.  
  5. NSLog(@ "success" );
  6.  
  7. } failure:^{
  8.  
  9.   
  10.  
  11. NSLog(@ "failure" );
  12.  
  13. }];

Chaining shows amazing advantages when there are many parameters and parameter choices or when there is a high probability of modification. Because it is very convenient to modify, just add or modify a method.

For example:

The centralized API package should look like this:

  1. - (void)GET:(NSString *)url
  2.  
  3. parameters:(id)Parameters
  4.  
  5. success:(SuccessBlock)success
  6.  
  7. failure:(FailureBlock)failure;

When you want to add a Version attribute to determine the API version, what can you do? You can only rewrite the method, add a Version parameter to the method, and then change the method for all network requests used.

We will not consider the comparison if we switch to distributed API encapsulation.

  1. GeneralAPI *apiGeGet = [[GeneralAPI alloc] initWithRequestMethod:@ "get" ];
  2.  
  3. apiGeGet.apiRequestMethodType = RequestMethodTypeGET;
  4.  
  5. apiGeGet.apiRequestSerializerType = RequestSerializerTypeHTTP;
  6.  
  7. apiGeGet.apiResponseSerializerType = ResponseSerializerTypeHTTP;
  8.  
  9. [apiGeGet setApiCompletionHandler:^(id responseObject, NSError * error) {
  10.  
  11. NSLog(@ "responseObject is %@" , responseObject);
  12.  
  13. if (error) {
  14.  
  15. NSLog(@ "Error is %@" , error.localizedDescription);
  16.  
  17. }
  18.  
  19. }];
  20.  
  21. [apiGeGet start];

Is this structure too loose?

Then change to WZXNetworking

All we need to do is add another method and a member variable, and then add .method() after the original method.

  1. - (WZXNetworkManager * (^) (id some ))method {
  2.  
  3. return ^WZXNetworkManager (id some ) {
  4.  
  5. self.XXX = some  
  6.  
  7. return self;
  8.  
  9. }
  10.  
  11. }
  1. [[WZXNetworkManager manager].setRequest(@ "http://192.168.1.40:8001" ).method( some ) startRequestWithSuccess:^(idresponse) {
  2.  
  3.   
  4.  
  5. NSLog(@ "success" );
  6.  
  7. } failure:^{
  8.  
  9.   
  10.  
  11. NSLog(@ "failure" );
  12.  
  13. }];

The code is here: WZXNetworking

https://github.com/Wzxhaha/WZXNetworking

As for how the chain is implemented, see WZXProgrammingIdeas

https://github.com/Wzxhaha/WZXProgrammingIdeas

<<:  Learn common Mac commands to help iOS development

>>:  Some methods to detect iOS APP performance

Recommend

The little assistant who served Bethune back then is now 101 years old!

At the age of 18, he worked as an assistant Fight...

How to leverage the Apple Store to promote your own App?

In addition to delivering a good product, how can...

Product analysis, function analysis | Bilibili Live!

Editor's Note: Since its listing , Bilibili (...

National Low Carbon Day丨What are the twelve hours of low carbon life like?

July 10 to 16 this year is the 33rd National Ener...

“Double-peak La Niña” is coming, will you be frozen to tears this winter?

From November 4 to 8, my country just experienced...

Yang Liang's English Vocabulary Memorization Guide 40 Episodes

Course Outline 00 The five most common mistakes C...

12306 user data has been leaked, change your password quickly!

[[125173]] Today, the vulnerability reporting pla...

Developers must know: Google made a difficult decision

Google has made a difficult decision: to deprecat...