Team collaboration and MVVM architecture design in iOS

Team collaboration and MVVM architecture design in iOS

I wrote this article today to serve as a starting point for discussion and exchange of ideas with you all, so that we can learn from each other. If there are any deficiencies in this blog, I hope you can criticize and correct me. The content of this article follows the style of previous blogs, which is mainly practical, with occasional nonsense (haha~ I’m not working hard and I’m starting to blog again~).

Due to my limited project experience and limited understanding of architectural design, my personal understanding of MVVM is mainly based on the MVC Web framework I used before. When I was in school, I used the ThinkPHP framework and the SSH framework, both of which are MVC architectural models. Today, MVVM and traditional MVC are extremely similar, and they can be said to be brothers, or family members.

Speaking of architecture design and team collaboration, this is quite important for App development. Even as a professional brick mover, the premise is where do you put the bricks after moving them? Not only Code has a framework, but other things also have frameworks, such as bridges and so on. I won't go into details here. A good engineering framework can not only improve the efficiency of team collaboration, but also reduce the redundancy and coupling of code. Reasonable division of labor and system architecture design are indispensable.

As for team collaboration, it is not enough to just have version control tools such as SVN or Git. As for how to use SVN in iOS development, please refer to the previous blog (iOS Development Version Control (SVN)). A team can work efficiently. I think communication is the most important thing. Everyone in the team is relatively friendly, and there are no obstacles in communication (but there are always a few people in some teams who don't get along). Communication is the most important thing in a team. As for how to use SVN, that's not a problem!

Okay, today I will use a demo I wrote to talk about the architectural design and team collaboration in iOS development. That’s all for today, let’s get into today’s topic.

In order to write today's blog, I spent some time to build a project. The backend interface of this project was tested using the Sina Weibo API. The GitHub sharing link will be posted later in this article. OK~ To put it in a more high-sounding way, everyone has their own opinions, so let's exchange ideas and learn together.

1. A little MVVM

I won't go into details about what MVC is, and what MVVM is. You can find a lot of information by searching on Baidu, so I'll just briefly mention it here. The following demo uses the MVVM architecture pattern.

The Model layer is indispensable. We need something to act as a DTO (Data Transfer Object). Of course, a dictionary is also possible. Programming requires flexibility. The Model layer is a relatively thin layer. If you have learned Java, you should be familiar with JavaBean.

The ViewModel layer is the glue between the View and Model layers. It is an excellent place to place user input validation logic, view display logic, network request initiation and other various codes. To put it simply, it is to separate the business logic and page logic of the original ViewController layer and put it in the ViewModel layer.

The View layer is the ViewController layer. Its task is to obtain data from the ViewModel layer and then display it.

The above is just a brief introduction to MVVM. If you want to understand and apply it well, you still need to practice it.

2. Discussion on whether to use StoryBoard in the project

I often see on the Internet that it is not recommended to use StoryBoard or Xib, and it is recommended to write it in pure code. I personally think that this view is contrary to the original intention of Apple to design StoryBoard. In the projects I have done, StoryBoard is the main one, and Xib is supplemented, and then each StoryBoard is integrated with code.

Let me give you an example of the benefits of using Storyboard. If you use Storyboard to add constraints to controls, it takes only a few seconds. However, adding constraints with code is disgusting. If you write it with pure code, you will spend a lot of time writing UI, and the technical content is relatively low. I personally think it is unnecessary. In teamwork, the partners responsible for UI development only need to have no one responsible for a Storyboard, and each develop their own. When submitting with SVN, just remove the check mark below (as shown below), and there will be no problem using Storyboard. Then integrate with code. If you add new resource files to your project, you need to submit the Project Setting file together with it when submitting with XCode's built-in SVN.

3. Practical MVVM (Group created with Xcode is a virtual folder. For ease of maintenance, it is recommended to create a physical folder and then introduce it manually)

1. Let's experience the MVVM architecture pattern through an example. The following is the first-level directory of the project. The interaction between each layer is implemented in the form of Block.

Project Catalog Description:

Request: The class that stores network requests in the folder. The specific implementation is given below.

Config: is the configuration file of the project

Resource: It is the resource file of the project, including image resources and Storyboard file resources.

Tools is: tool file class, which stores tool classes, such as data regular matching, etc.

Vender: stores third-party libraries

Model: I won’t say much about this

ViewController: stores ViewController class resource files, that is, the View layer

ViewModel: stores various business logic and network requests

2. Detailed explanation of Request: Request is responsible for network requests, as follows:

NetRequestClass is the code for storing network requests. This project uses AF. Because this project is just a Demo, it only encapsulates the monitoring network status, GET request, and POST request methods. According to actual needs, it can also encapsulate upload and download methods.

The code in NetRequestClass.h is as follows:

  1. //  
  2. // NetRequestClass.h  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/6.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. # import   @interface NetRequestClass : NSObject
  10.   
  11. #pragma monitor network linkability
  12. + (BOOL) netWorkReachabilityWithURLString:(NSString *) strUrl;
  13.   
  14. #pragma POST request
  15. + ( void ) NetRequestPOSTWithRequestURL: (NSString *) requestURLString
  16. WithParameter: (NSDictionary *) parameter
  17. WithReturnValeuBlock: (ReturnValueBlock) block
  18. WithErrorCodeBlock: (ErrorCodeBlock) errorBlock
  19. WithFailureBlock: (FailureBlock) failureBlock;
  20.   
  21. #pragma GET request
  22. + ( void ) NetRequestGETWithRequestURL: (NSString *) requestURLString
  23. WithParameter: (NSDictionary *) parameter
  24. WithReturnValeuBlock: (ReturnValueBlock) block
  25. WithErrorCodeBlock: (ErrorCodeBlock) errorBlock
  26. WithFailureBlock: (FailureBlock) failureBlock;
  27.   
  28. @end  

The code in NetRequestClass.m is as follows:

  1. //  
  2. // NetRequestClass.m  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/6.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. # import   "NetRequestClass.h"  
  10.   
  11. @interface NetRequestClass ()
  12.   
  13. @end  
  14.   
  15.   
  16. @implementation NetRequestClass
  17. #pragma monitor network linkability
  18. + (BOOL) netWorkReachabilityWithURLString:(NSString *) strUrl
  19. {
  20. __block BOOL netState = NO;
  21.       
  22. NSURL *baseURL = [NSURL URLWithString:strUrl];
  23.       
  24. AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
  25.       
  26. NSOperationQueue *operationQueue = manager.operationQueue;
  27.       
  28. [manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
  29. switch (status) {
  30. case AFNetworkReachabilityStatusReachableViaWWAN:
  31. case AFNetworkReachabilityStatusReachableViaWiFi:
  32. [operationQueue setSuspended:NO];
  33. netState = YES;
  34. break ;
  35. case AFNetworkReachabilityStatusNotReachable:
  36. netState = NO;
  37. default :
  38. [operationQueue setSuspended:YES];
  39. break ;
  40. }
  41. }];
  42.       
  43. [manager.reachabilityManager startMonitoring];
  44.       
  45. return netState;
  46. }
  47.   
  48.   
  49. /****************************************
  50. Make a judgment here if there is an errorCode in the dic
  51. Call errorBlock(dic)
  52. If there is no errorCode, call block(dic
  53. ******************************/  
  54.   
  55. #pragma --mark GET request method
  56. + ( void ) NetRequestGETWithRequestURL: (NSString *) requestURLString
  57. WithParameter: (NSDictionary *) parameter
  58. WithReturnValeuBlock: (ReturnValueBlock) block
  59. WithErrorCodeBlock: (ErrorCodeBlock) errorBlock
  60. WithFailureBlock: (FailureBlock) failureBlock
  61. {
  62. AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init];
  63.       
  64. AFHTTPRequestOperation *op = [manager GET:requestURLString parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject) {
  65. NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
  66. DDLog(@ "%@" , dic);
  67.           
  68. block(dic);
  69.           
  70. } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  71. failureBlock();
  72. }];
  73.       
  74. op.responseSerializer = [AFHTTPResponseSerializer serializer];
  75.       
  76. [op start];
  77.       
  78. }
  79.   
  80. #pragma --mark POST request method
  81.   
  82. + ( void ) NetRequestPOSTWithRequestURL: (NSString *) requestURLString
  83. WithParameter: (NSDictionary *) parameter
  84. WithReturnValeuBlock: (ReturnValueBlock) block
  85. WithErrorCodeBlock: (ErrorCodeBlock) errorBlock
  86. WithFailureBlock: (FailureBlock) failureBlock
  87. {
  88. AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init];
  89.       
  90. AFHTTPRequestOperation *op = [manager POST:requestURLString parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject) {
  91. NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
  92.           
  93. DDLog(@ "%@" , dic);
  94.           
  95. block(dic);
  96. /******************************************
  97. Make a judgment here if there is an errorCode in the dic
  98. Call errorBlock(dic)
  99. If there is no errorCode, call block(dic
  100. ******************************/  
  101.           
  102. } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  103. failureBlock();
  104. }];
  105.       
  106. op.responseSerializer = [AFHTTPResponseSerializer serializer];
  107.       
  108. [op start];
  109.   
  110. }
  111.   
  112.   
  113.   
  114.   
  115. @end  

3. Detailed explanation of Config: creating pch files and Config.h files

The pch file introduces commonly used header files, the contents are as follows:

  1. //  
  2. // PrefixHeader.pch  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/6.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. #ifndef MVVMTest_PrefixHeader_pch
  10. #define MVVMTest_PrefixHeader_pch
  11.   
  12. # import "AFNetworking.h"  
  13. # import   "UIKit+AFNetworking.h"  
  14. # import   "Config.h"  
  15.   
  16. # import   "NetRequestClass.h"  
  17. # import   "SVProgressHUD.h"  
  18. #endif

Config.h contains various macro definitions, enumeration types, and block types. The code is as follows:

  1. //  
  2. //Config.h  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/6.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. #ifndef MVVMTest_Config_h
  10. #define MVVMTest_Config_h
  11.   
  12. //Define the block type that returns the requested data  
  13. typedef void (^ReturnValueBlock) (id returnValue);
  14. typedef void (^ErrorCodeBlock) (id errorCode);
  15. typedef void (^FailureBlock)();
  16. typedef void (^NetWorkBlock)(BOOL netConnetState);
  17.   
  18. #define DDLog(xx, ...) NSLog(@ "%s(%d): " xx, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
  19.   
  20. //accessToken  
  21. #define ACCESSTOKEN @ "your own access_token"  
  22.   
  23. //Request the network interface of the public microblog  
  24. #define REQUESTPUBLICURL @ "https://api.weibo.com/2/statuses/public_timeline.json"  
  25.   
  26. #define SOURCE @ "source"  
  27. #define TOKEN @ "access_token"  
  28. #define COUNT @ "count"  
  29.   
  30. #define STATUSES @ "statuses"  
  31. #define CREATETIME @ "created_at"  
  32. #define WEIBOID @ "id"  
  33. #define WEIBOTEXT @ "text"  
  34. #define USER @ "user"  
  35. #define UID @ "id"  
  36. #define HEADIMAGEURL @ "profile_image_url"  
  37. #define USERNAME @ "screen_name"  
  38.   
  39. #endif

4. Detailed explanation of resource file Resource, the structure is as follows:

Image stores various images (3x, 2x, etc.), InterfaceBuilder stores some Xib and Storyboard files, and each developer responsible for UI is responsible for a Storyboard.

5. Detailed explanation of Model: This project uses the public Weibo interface to request. We need to display the user's avatar, username, release date, blog post, implicit user ID and Weibo ID on the page. The file directory structure is as follows:

The content of PublicModel is as follows:

  1. //  
  2. // PublicModel.h  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/8.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. # import   @interface PublicModel : NSObject
  10. @property (strong, nonatomic) NSString *userId;
  11. @property (strong, nonatomic) NSString *weiboId;
  12. @property (strong, nonatomic) NSString *userName;
  13. @property (strong, nonatomic) NSURL *imageUrl;
  14. @property (strong, nonatomic) NSString *date;
  15. @property (strong, nonatomic) NSString *text;
  16.   
  17. @end  

6. Detailed explanation of the ViewModel layer. This layer is the most important layer. The following is a detailed screenshot of this layer. ViewModeClass is the parent class of all ViewModes, which stores the common parts

The content of ViewModelClass.h is as follows:

  1. //  
  2. // ViewModelClass.h  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/8.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. # import   @interface ViewModelClass : NSObject
  10.   
  11. @property (strong, nonatomic) ReturnValueBlock returnBlock;
  12. @property (strong, nonatomic) ErrorCodeBlock errorBlock;
  13. @property (strong, nonatomic) FailureBlock failureBlock;
  14.   
  15.   
  16. //Get the network connection status  
  17. -( void ) netWorkStateWithNetConnectBlock: (NetWorkBlock) netConnectBlock WithURlStr: (NSString *) strURl;
  18.   
  19. //Block block for incoming interaction  
  20. -( void ) setBlockWithReturnBlock: (ReturnValueBlock) returnBlock
  21. WithErrorBlock: (ErrorCodeBlock) errorBlock
  22. WithFailureBlock: (FailureBlock) failureBlock;
  23. @end  

The contents of ViewModelClass.m are as follows:

  1. //  
  2. // ViewModelClass.m  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/8.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. # import   "ViewModelClass.h"  
  10. @implementation ViewModelClass
  11.   
  12. #pragma Get network reachability status
  13. -( void ) netWorkStateWithNetConnectBlock: (NetWorkBlock) netConnectBlock WithURlStr: (NSString *) strURl;
  14. {
  15. BOOL netState = [NetRequestClass netWorkReachabilityWithURLString:strURl];
  16. netConnectBlock(netState);
  17. }
  18.   
  19. #pragma Receive the block that passes through
  20. -( void ) setBlockWithReturnBlock: (ReturnValueBlock) returnBlock
  21. WithErrorBlock: (ErrorCodeBlock) errorBlock
  22. WithFailureBlock: (FailureBlock) failureBlock
  23. {
  24. _returnBlock = returnBlock;
  25. _errorBlock = errorBlock;
  26. _failureBlock = failureBlock;
  27. }
  28.   
  29. @end  

The contents of PublicWeiboViewModel.m are as follows:

  1. //  
  2. // PublicWeiboViewModel.m  
  3. //MVVMTest  
  4. //  
  5. // Created by Li Zelu on 15/1/8.  
  6. // Copyright (c) 2015 Li Zelu. All rights reserved.  
  7. //  
  8.   
  9. # import   "PublicWeiboViewModel.h"  
  10. # import   "PublicDetailViewController.h"  
  11.   
  12. @implementation PublicWeiboViewModel
  13.   
  14. //Get public microblog  
  15. -( void ) fetchPublicWeiBo
  16. {
  17. NSDictionary *parameter = @{TOKEN: ACCESSTOKEN,
  18. COUNT: @ "100"  
  19. };
  20. [NetRequestClass NetRequestGETWithRequestURL:REQUESTPUBLICURL WithParameter:parameter WithReturnValeuBlock:^(id returnValue) {
  21.           
  22. DDLog(@ "%@" , returnValue);
  23. [self fetchValueSuccessWithDic:returnValue];
  24.           
  25. } WithErrorCodeBlock:^(id errorCode) {
  26. DDLog(@ "%@" , errorCode);
  27. [self errorCodeWithDic:errorCode];
  28.           
  29. } WithFailureBlock:^{
  30. [self netFailure];
  31. DDLog(@ "Network abnormality" );
  32.           
  33. }];
  34.       
  35. }
  36.   
  37.   
  38.   
  39. #pragma Get the correct data and process the correct data
  40. -( void )fetchValueSuccessWithDic: (NSDictionary *) returnValue
  41. {
  42. //Process the data obtained from the background and then pass it to the ViewController layer for display  
  43.       
  44. NSArray *statuses = returnValue[STATUSES];
  45. NSMutableArray *publicModelArray = [[NSMutableArray alloc] initWithCapacity:statuses.count];
  46.       
  47. for ( int i = 0 ; i < statuses.count; i ++) {
  48. PublicModel *publicModel = [[PublicModel alloc] init];
  49.           
  50. //Set the time  
  51. NSDateFormatter *iosDateFormater=[[NSDateFormatter alloc]init];
  52. iosDateFormater.dateFormat=@ "EEE MMM d HH:mm:ss Z yyyy" ;
  53.           
  54. //Must be set, otherwise it cannot be parsed  
  55. iosDateFormater.locale=[[NSLocale alloc]initWithLocaleIdentifier:@ "en_US" ];
  56. NSDate *date=[iosDateFormater dateFromString:statuses[i][CREATETIME]];
  57.           
  58. //Destination format  
  59. NSDateFormatter *resultFormatter=[[NSDateFormatter alloc]init];
  60. [resultFormatter setDateFormat:@ "MM month dd day HH:mm" ];
  61.           
  62. publicModel.date = [resultFormatter stringFromDate:date];
  63. publicModel.userName = statuses[i][USER][USERNAME];
  64. publicModel.text = statuses[i][WEIBOTEXT];
  65. publicModel.imageUrl = [NSURL URLWithString:statuses[i][USER][HEADIMAGEURL]];
  66. publicModel.userId = statuses[i][USER][UID];
  67. publicModel.weiboId = statuses[i][WEIBOID];
  68.           
  69. [publicModelArray addObject:publicModel];
  70.           
  71. }
  72.       
  73. self.returnBlock(publicModelArray);
  74. }
  75.   
  76. #pragma handles ErrorCode
  77. -( void ) errorCodeWithDic: (NSDictionary *) errorDic
  78. {
  79. self.errorBlock(errorDic);
  80. }
  81.   
  82. #pragma handles network exceptions
  83. -( void ) netFailure
  84. {
  85. self.failureBlock();
  86. }
  87.   
  88.   
  89. #pragma Jump to the details page. If a network request is required, you can add the corresponding network request in this method.
  90. -( void ) weiboDetailWithPublicModel: (PublicModel *) publicModel WithViewController:(UIViewController *)superController
  91. {
  92. DDLog(@ "%@,%@,%@" ,publicModel.userId,publicModel.weiboId,publicModel.text);
  93. UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@ "Main" bundle:[NSBundle mainBundle]];
  94. PublicDetailViewController *detailController = [storyboard instantiateViewControllerWithIdentifier:@ "PublicDetailViewController" ];
  95. detailController.publicModel = publicModel;
  96. [superController.navigationController pushViewController:detailController animated:YES];
  97.       
  98. }
  99.   
  100.   
  101. @end  

7. The directory structure of the ViewController layer is as follows:

I won't paste the above codes one by one (mainly because I'm tired of pressing command + C by hand), the link below will have the source code

8. The structure in storybord is as follows:

The final result of running:

9. The complete directory structure, business logic between pages, and network request data are placed in the ViewModel layer. Of course, this is not absolute and needs to be handled flexibly. I personally like programming very much because it is fun to be flexible in programming.

10. The article is almost over. Here I will summarize the most frequently used and important commands of SVN (of course, I usually use the SVN visual management that comes with XCode~)

(1) Update local code command

svn up Update version

svn info View the current version information

(2) Code submission

svn info View the current local version information

svn up Update to the latest version information

svn st View file status M-modify D-delete A-add U-update?-unknown status!-warning C-conflict

svn add fileName: If it appears? Usually it is a problem when adding files. After svn add fileName, the status of the file will change to A

svn del fileName: If it appears! It is usually a warning that appears when deleting a file. After executing the svn del fileName command, the status of the file will change to D

svn ci -m "Reason for commit"

<<:  iOS upgrades stagnate, app crash rates slowly decrease

>>:  Messaging apps will become the next generation of Internet portals

Recommend

Channel marketing rules for new brand snacks!

Snacks have been popular since ancient times. Now...

How to plan fission activities? 4 key nodes!

The key to planning fission activities lies in th...

After grains and vegetables, can we also “ask for food from the forest”?

The autumn breeze brings the fragrance of fruits ...

How does Snapchat survive amid competition from Facebook and other products?

Snapchat has been facing pressure from a number o...

Why are iPhone 6 and iPhone 6+ so easy to bend?

As someone who has worked on mobile phone structu...