IOS MVVM+RAC from framework to combat

I. Introduction

Two, talk about MVVM and RAC
1, MVVM analysis of
2, RAC shallow analysis of
, this article on the understanding of the use of both the 3

Two, part of the framework of
2, the base framework directory Xiangjie
3, digression

Three, the actual part (classic list)

LSCircleListViewController 1, treatment 2, View 3, LSCircleListModel 4, ViewModel 5, APPDelegate code is simplified

Four, postscript

Five, Demo out

I. Introduction

Long before I want to write about yourself in the framework of ideas, but always feel well, after all, in the field of iOS more deeply felt more of their ignorance, the hearts of awe, no more confidence to write this thing (you can also understand is no time (>, <), please forgive me I drunby, hehe). The architecture model of people love to hate this thing, the fact is that simple, but in the eyes of one thousand individuals have one thousand kinds of Hamlett, he said it is the fact that the myriads of changes, and when you are deeply involved you really addictive, and never tired! A few days ago to write a "iOS Xcode" a comprehensive analysis of the amount of reading in just one day this thousand, but also on the Jane book page (you see this sentence font is not know ad (* o *)), really very happy with my friend yesterday, with the code to explain me on MVVM understanding and use of the circumstances, a hot head with an article to share with everyone is excusable, certainly hope more God to show you, let me also let everybody have promotion enough, thank you very much!

Two, talk about MVVM and RAC

1, MVVM analysis

Here I have seen your default MVVM articles (after all related articles can be used to describe the sky flying ~ (= at below) / ~ la la la!) Just a brief account of my understanding of it.

MVC is the standard model of iOS App, is a paradigm used to organize code authority Apple recommended on the market, most of the App is so constructed that the specific formation mode does not elaborate, iOS beginners are familiar (though not necessarily to follow, but it cannot avoid some) the problem is very serious problems developers such as heavy ViewController, lost the logic of the network (not where it belongs), poor testability, etc. it will also maintain a new MVVM architecture, strong coupling is very low (MVC a new pop architecture).

Although MVVM is derived from Microsoft, it should not be opposed to it, which formally standardizes the formal specification of the properties of the view and controller tightly coupled, as shown below:

IOS MVVM+RAC from framework to combat

ViewModel: is a new view model compared to MVC. Is the view of logic, logic verification, network request code stored somewhere, the only thing to note is that any references to the view itself should not be placed in the VM, in other words, VM do not introduce UIKit.h (for image this can be seen as data processing, which depends on personal ideas. That does not affect the overall architecture).

In this way, we solve the VC problem of the logic code bloated, and network requests are written in VM, and because VM contains all the display logic and not using V, so it can be fully tested by programming.

So, that’s what it looks like, 6666!

2, RAC shallow analysis

Particularly shallow… This paper focuses on the framework and the actual combat and the idea of MVVM, RAC said it’s long learning curve, it is difficult to understand, not a good start, because before learning when the user, Chinese tutorial is still relatively small, so it is difficult to learn to use, (then indeed spent a great strength to strength, loaded to force a @%& $%&amp but now mature); bad street, as long as the heart, a lot of good tutorials, can dive down to see the hydrology I write one, take the RAC be nothing difficult!

ReactiveCocoa can be said to be a combination of functional programming and response framework programming, it also can be called response function programming (FRP) framework, emphasize that although RAC is the biggest advantage of providing a single, unified approach to deal with asynchronous behavior, including delegate method, blocks callback, target-action the mechanism of notifications and KVO., but not simply just think he is simply reducing code complexity, better with MVVM only, young man, so you look down on it.

It is the largest out of the ordinary provides a new thinking to write the code, because the RAC Cocoa KVO UIKit, will event, delegate, selector and increase the RAC support, so do not have to do a lot of cross function.

If the project is using RAC to realize, for the same business logic can be finally completed in the same block of code, the UI event, logic processing, file or database operation, asynchronous network requests, UI results showed that a large set of all with functional programming ideas set up into the page to build a good relationship of all this, after the user clicks properly with this set of links one by one according to the desired logical order and trigger, finally displayed to the user.

Well, said so much, and no head ~ (more than = at / ~ la la la)!

3, the understanding of the use of both

In the introduction, will use the combination of MVVM+RAC, fix a list add load and pull-down refresh, so more interpretation MVVM, instead of RAC logic operation chain (a point with the login screen to write more can reflect the Y^o^Y), RAC plays the greater part of the role is the better decoupling, reduce code complexity, make the code clear and clear logic more convenient for maintenance and upgrade.

Two, the framework part

1, the framework directory

First of all, the framework of the directory structure, as follows

IOS MVVM+RAC from framework to combat

1, Frameworks

Virtual folder system library, the current time framework need to manually add a name to the Frameworks virtual folder, so you add in the Build database system in Phases will automatically fall into this folder, not directly on the external display that upset the directory structure. The system library add process is as follows:

IOS MVVM+RAC from framework to combat

In addition, the careful guy will find that there are two identical Frameworks in this directory, so what the hell is this? The Frameworks is on the top in his frame to add their own, the project also is very simple, not so naughty, the problem in the following Pods Target, folder will automatically give you generate a virtual Frameworks to add it, it should ask why not directly use the??? There’s so much nonsense! Since there is no conflict, you can keep it “(like this), person)

Since the mention of Pods, then talk about CocoaPods (third party library management tools).

2, CocoaPods

When you develop iOS applications, you will often use a lot of third party open source libraries, such as JSONKit, AFNetWorking, etc.. A library and use other libraries, so you want to use it, must also download other libraries and other libraries and use other libraries, “endless generation after generation of descendants also”, it is in my early experience this pain, good sad, one by one to manually download the required library is very troublesome.

There is also a common situation is that your project used in the library is updated, you have to re download the new version, re added to the project, very troublesome.

CocoaPods is to help you solve the problem above, saying it should be iOS the most famous library management tools, as iOS programmers we master the use of CocoaPods is a basic skill for this thing, how to use?

O (a _ U) O ha ~ do you think I will tell you? Well, I this person is very soft, following a map to tell you how to use… (on for grinding goblins)

IOS MVVM+RAC from framework to combat

(- – ‘) Oh, good oh ~ get is not a new skill? 6666!

3, AppDelegate

This directory is the decentralization of AppDelegate.h (.M) file, the entire application is the entry file, so take it out alone. After a while to tell you how to write a simple AppDelegate, it will add some classes in this folder, so it is necessary to put it in a folder.

4, Class

The main body of the project, most of the daily development code here, but also a lot of sub sub directory.

generic class

  • General: General (process folder does not need to change the project transfer can be directly used by the Base class (class): the framework of Categories): public extension (that is, some common categories, such as sharing what ah): Core (general public core storing personal information and interface API): Models public Model (some common data model (Views): public View package of some common View)

Tool class

  • Helpers: engineering related auxiliary class (such as similar data request, form upload, network monitoring tools)

Macro definition class

  • Macro: macro definition class (that is, the application will use the AppMacro.h app macro definition) project related macro definition NotificationMacro.h notice related macro definition of third party VendorMacro.h UtilsMacro.h was defined as macros to simplify the code… Etc. (other as you! Y^o^Y)

APP specific module code class

  • Sections: the module folder (in general, our human units) LSSections Wang Longshuai Ma Chenglin CLSections folder folder… Etc. (you can also write the love Cang teacher, Diao Diao!)

Each member of the folder under the folder is responsible for its modules, such as the teacher is responsible for the PHP interface module (I think PHP is the best language! You can talk about it in the comments section! On the one grinding goblins), are as follows (then the personal folders above):

  • PHP: the name of the module, can also be home (HomePage)… And so on ViewControllers interface controller (which is stored at the folder name) ViewModels char (core, MVVM, decoupling processing logic) Views interface related View storage (View interface) Models data storage model (data model, all alone not a little fat, thin is the standard Model)

This is the standard MVVM… Why don’t you join the list above? Why? Why? Because the minister can not do ah!!! Not three, the list of four MarkDown writing, seeking God weapon! Most have thanks again!)

Third party Library

  • Vendors: third party library /SDK, such as UMeng, WeiboSDK, WeixinSDK, etc..

To this man and the heart of the doubt, nagging: (to ‘) What are you get what! Just just told a third party library management CocoaPods, you’re here and engage in a (‘,’) believe it or not I was you!

Ha ha ha, just CocoaPods does most of the management of third party libraries, there are two reasons here to set up the third party Library Directory: first, not all you need third party support pods, so still need to manually add some library. Second, some third party libraries, although support for pods, but we need to change or even customize the third party, this time also need to be placed here, but also to prevent the use of pods accidentally update your custom! () you to hit me ah!

5, Resource

Here are some of the resources required for the project, as follows

  • Fonts fonts
  • Images picture (of course you can add to Assets.xcassets, no one stopped you)
  • Sounds sound
  • Videos video

OK, here’s the list! Want to know more details can contact me!

2, the base class

Here to explain the VC, V, VM base class, the other mode is similar to the View, so skip, where the base class of TableViewCell is a little special also mention.

My current base class as shown below:

IOS MVVM+RAC from framework to combat

Is not dazzled by…, I have also read it not pleasing to the eye, tried to get rid of all the base class, and then encountered some trouble… They compromise, at the end of the article, can I talk with you is how to get rid of the base class, and then fail, here we explain the base class.

1, YDViewController

IOS MVVM+RAC from framework to combat
IOS MVVM+RAC from framework to combat

The specific purpose of the function has been very clear, here simply talk about the role of four functions

  • Yd_addSubviews: add View to ViewController
  • Yd_bindViewModel: used to bind V (VC) with VM
  • Yd_layoutNavigation: set the navigation bar and column
  • Yd_getNewData: call when the first data acquisition (not particularly necessary)

2, YDView

IOS MVVM+RAC from framework to combat
IOS MVVM+RAC from framework to combat
  • Yd_setupViews: add sub View to master View
  • Yd_bindViewModel: bind V with VM
  • Yd_addReturnKeyBoard: set click the blank keyboard recovery

3, YDViewModel

IOS MVVM+RAC from framework to combat
IOS MVVM+RAC from framework to combat
  • Yc_initialize: some logical binding, network data request processing.
  • LSRefreshDataStatus data processing is required after the operation identification and more data LSHeaderRefresh_HasNoMoreData LSHeaderRefresh_HasMoreData drop-down drop-down no more LSFooterRefresh_HasMoreData data and more data on LSFooterRefresh_HasNoMoreData pull pull no more data LSRefreshError LSRefreshUI UI layout just refresh refresh error

4, YDTableViewCell

IOS MVVM+RAC from framework to combat

Because the Cell is more special, so just take it out and say. The base class is observed above ViewMdoel and View will find that each base class will have data binding, but the cell when data binding needs to be placed in the initialization data, because the data binding logic all base classes are invoked when there is no return to initialize the object, but if there are cell data binding there will be problems such as below:

IOS MVVM+RAC from framework to combat
cell multiplexing failed

If the above function is in the range of bindViewModel, it will reuse failure, click on the button is no response, but if it is invoked when the data initialization: for example, when the setViewModel will be OK, because of which cell used in RAC multiplexing mechanism rac_prepareForReuseSignal, and not when the initialization is returned in failure cell.

3, digression

The base class is the role of unified management, unified style, easy encoding, more additional function, recommend the use of Protocol or Category, such as portability, convenient management and expansion, so as not to affect the situation as a whole.

The base core is used to configure the V VM (VC), and provide some necessary methods to deal with Protocol interface, logic, code style standardization, the function of each part is clear, so, when you need to write what, need to find what needs to change, what time will be very clear these code position, logic more clear, without wasting more time in thinking should be written where, where to find, to change the place where this is not the time consuming problem.

Three, the actual part (the realization of the classic list)

Here is the code structure of the interface is as follows, a list of very common: (too lazy to write, this is an interface, I do a project before the class before explaining will see is the beginning of the YD, here is the YC at the beginning of this distinction.)

IOS MVVM+RAC from framework to combat

First of all, observe the interface, the demand is: the amount of content of the head is more or less can be left and right sliding, and then the whole can pull on the loading. I deal with this: first, the whole interface is a TableView, and then divided into a Header, a Section and the main list Row. A CollectionView is nested on the Header to ensure reuse. Specific stratification as follows

IOS MVVM+RAC from framework to combat

Then the processed directory is as follows:

IOS MVVM+RAC from framework to combat

A brief introduction:

  • ViewController LSCircleListViewController: interface controller, responsible for jump, Navgation, TabBar, etc.
  • View LSCircleListView: View interface, LSCircleListHeaderView display for the main interface: head Header package contains a CollectionView internal LSCircleListCollectionCell: head Header CollectionView in LSCircleListSectionHeaderView SectionView, custom Cell: this interface does not need reuse, so only a View can, if you need to reuse LSCircleListTableCell: TableView Cell TableViewHeaderFooterView
  • ViewModel LSCircleListViewModel: ViewModel LSCircleListHeaderViewModel: the main interface head Header corresponding ViewModel LSCircleListCollectionCellViewModel: CollectionCell TableViewCell ViewModel (the head and because the data structure of the two is the same: Section ViewModel LSCircleListSectionHeaderViewModel)
  • Model LSCircleListModel: circle data model (header and tableViewCell data structure is consistent)

A small interface so many classes is not difficult to accept, calm, Sao year! You have to think about these things on VC within what is like? And a few thousand lines! A little exaggeration! But enough headache), so many classes here focus on to talk about the main VC, V, VM, the main main main M OK, with clear MVVM is how to work will follow.

1, LSCircleListViewController processing

First code:

LSCircleListViewController.m / / ZhongShui / / / / / / / / Created / / Copyright 16/3/10. by Wang Longshuai on of All rights reserved. in 2016. Wang Longshuai / / #import "LSCircleListViewController.h" #import "LSCircleListView.h" #import "LSCircleListViewModel.h" #import "LSCircleMainPageViewController.h" #import "LSCircleMainPageViewModel.h" #import "LSCircleListCollectionCellViewModel.h" #import "LSNewCircleListViewController.h" @interface (LSCircleListViewController) @property (nonatomic, strong) LSCircleListView *mainView; @property (nonatomic, strong) LSCircleListViewModel *viewModel; @end @implementation LSCircleListViewController (void) viewDidLoad {[super viewDidLoad]; Do any additional setup after loading / the view. #pr} AGMA mark - System - updateViewConstraints (void) {WS (weakSelf) [self.mainView mas_makeConstraints:^ (MASConstraintMaker *make) {make.edges.equalTo (weakSelf.view);}]; [super updateViewConstraints];} #pragma mark - Private - yc_addSubviews (void) {[self.view addSubview:self.mainView];} - {@weakify (void) yc_bindViewModel (self); [[self.viewModel.cellClickSubject takeUntil:self.rac_willDeallocSignal] subscribeNext:^ (LSCircleListCollectionCellViewModel *viewModel) {@strongify (self); LSCircleMainPageViewModel *mainViewModel = [[LSCircleMainPageViewModel alloc] init]; mainViewModel.headerViewModel.circleId = viewModel.idStr; mainViewModel.headerViewModel.headerImag EStr = viewModel.headerImageStr; mainViewModel.headerViewModel.title =; mainViewModel.headerViewModel.numStr = viewModel.peopleNum; LSCircleMainPageViewController = *circleMainVC [[LSCircleMainPageViewController alloc] initWithViewModel:mainViewModel]; [self.rdv_tabBarController setTabBarHidden:YES animated:YES]; [self.navigationController pushViewController:circleMainVC animated:YES];}]; [self.viewModel.listHeaderViewModel.addNewSubject subscribeNext:^ (ID x) {@strongify (self); LSNewCircleListViewController *newCircleListVC = [[LSNewCircleListViewController alloc] init]; [self.rdv_tabBarController setTabBarHidden:YES animated:YES]; [self.nav IgationController pushViewController:newCircleListVC animated:YES];}] - (void);} yc_layoutNavigation {self.title = @ "circle list"; [self.rdv_tabBarController setTabBarHidden:NO animated:YES];} #pragma - layzLoad - mark (LSCircleListView * mainView) {if (_mainView! = [[LSCircleListView) {_mainView alloc] initWithViewModel:self.viewModel]}; return _mainView;} - {if (LSCircleListViewModel * viewModel) (! _viewModel _viewModel = LSCircleListViewModel alloc]) {init]} "; return _viewModel;} - {(void) didReceiveMemoryWarning [super didReceiveMemoryWarning] Dispose of any resources that; / / can be recreated.} / * #pragma mark - Navigation a Storyboard-based app / / In Lication, you will often want to do a little preparation before navigation (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (ID) sender the new view {/ / Get controller using [segue destinationViewController]. Pass selected object to the / the new / @end view controller.}

For VC, is divided into three modules, respectively, for the following:

I first module: system function

IOS MVVM+RAC from framework to combat

This function is from the beginning of the iOS6.0 in the ViewController to add a new constraint layout method, the default implementation of this method is to call the corresponding View updateConstraints. The ViewController View will first call the ViewController updateViewConstraints method when updating the view layout. We can rewrite this method to update the current View’s internal layout without having to inherit the View to override the -updateConstraints method. When we override this method, be sure to call super or call the current View -updateConstraints method.

Second, a private function module:

IOS MVVM+RAC from framework to combat

The specific functions of these three functions are also mentioned in the previous base class

  • Yd_addSubviews: add View to ViewController
  • Yd_bindViewModel: here are two jump events.
  • Yd_layoutNavigation: set the title to “circle list”, and TabBar does not hide

Third modules: lazy loading

IOS MVVM+RAC from framework to combat

This does not need to explain, and then reload.

2, View processing

First code

LSCircleListView.m / / ZhongShui / / / / / / / / Created / / Copyright 16/3/10. by Wang Longshuai on of All rights reserved. in 2016. Wang Longshuai / / #import "LSCircleListView.h" #import "LSCircleListViewModel.h" #import "LSCircleListHeaderView.h" #import "LSCircleListSectionHeaderView.h" #import "LSCircleListTableCell.h" @interface (LSCircleListView) < UITableViewDataSource, UITableViewDelegate> @property (strong, nonatomic *viewModel @property (LSCircleListViewModel); strong nonatomic, UITableView *mainTableView); @property (strong, nonatomic) LSCircleListHeaderView *listHeaderView @property (strong, nonatomic); LSCircleListSectionHeaderView *sectionHeaderView; @end @implementation LSCircleListView override drawRect: if / / Only / * You perform custom drawing. An empty implementation adversely affects performance / during animation. - (void) drawRect: (CGRect rect) {/ / Drawing / #pragma mark code} - System - (instancetype) initWithViewModel: (id< YCViewModelProtocol& gt; viewModel) {self.viewModel = (LSCircleListViewModel * viewModel); return [super initWithViewModel:viewModel];} - (void) updateConstraints {WS (weakSelf) [self.mainTableView mas_makeConstraints:^ (MASConstraintMaker *make) {make.edges.equalTo (weakSelf);}]; [super updateConstraints];} #pragma mark - Private - yc_setupViews (void) {[self addSubview:self.mainTableView]; [self setNeedsUpdateConstraints]; [self updateConstraintsIfNeeded];} - (void) Yc_bindViewModel {[self.viewModel.refreshDataCommand execute:nil]; @weakify (self); [self.viewModel.refreshUI subscribeNext:^ (ID x) {@strongify (self); [self.mainTableView reloadData];}]; [self.viewModel.refreshEndSubject subscribeNext:^ (ID x) {@strongify (self); [self.mainTableView reloadData]; switch ([x integerValue]) {case {LSHeaderRefresh_HasMoreData: [self.mainTableView.mj_header endRefreshing]; if (self.mainTableView.mj_footer = = Nil) {self.mainTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{@strongify (self); [self.viewM Odel.nextPageCommand execute:nil];}];}} {[self.mainTableView.mj_header break; case LSHeaderRefresh_HasNoMoreData: endRefreshing]; self.mainTableView.mj_footer = nil;} case {break; LSFooterRefresh_HasMoreData: [self.mainTableView.mj_header endRefreshing] [self.mainTableView.mj_footer resetNoMoreData]; [self.mainTableView.mj_footer endRefreshing]; break case;} LSFooterRefresh_HasNoMoreData: {[self.mainTableView.mj_header; endRefreshing]; self.mainTableView.mj_footer [e NdRefreshingWithNoMoreData]; break case {[self.mainTableView.mj_footer}; LSRefreshError: endRefreshing]; [self.mainTableView.mj_header endRefreshing]; break default: break;};}}}]; #pragma - lazyLoad - mark (LSCircleListViewModel * viewModel) {if (_viewModel! = [[LSCircleListViewModel) {_viewModel alloc] init]}; return _viewModel;} - (UITableView * mainTableView) {if (_mainTableView!) [[UITableView alloc] {_mainTableView = init]; _mainTableView.delegate = self; _mainTableView.dataSource = self; _mainTableView.backgroundColor = GX _BGCOLOR; _mainTableView.separatorStyle = UITableViewCellSeparatorStyleNone; _mainTableView.tableHeaderView = self.listHeaderView; [_mainTableView registerClass:[LSCircleListTableCell class] forCellReuseIdentifier:[NSString stringWithUTF8String: object_getClassName ([LSCircleListTableCell class]) "; WS (weakSelf) _mainTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{[weakSelf.viewModel.refreshDataCommand execute:nil];}]; _mainTableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{[weakSelf.viewModel.nextPageCommand execute:nil]}]; return _mainTableView;};} - (LSCircleListHeaderView * listHead) ErView {if (_listHeaderView! = [[LSCircleListHeaderView) {_listHeaderView alloc] initWithViewModel:self.viewModel.listHeaderViewModel]; _listHeaderView.frame = CGRectMake (0, 0, SCREEN_WIDTH, 160);} return _listHeaderView;} - (LSCircleListSectionHeaderView * sectionHeaderView) {if (_sectionHeaderView! = [[LSCircleListSectionHeaderView) {_sectionHeaderView alloc] initWithViewModel:self.viewModel.sectionHeaderViewModel]}}; return _sectionHeaderView; #pragma mark - delegate #pragma mark - UITableViewDataSource - (NSInteger) numberOfSectionsInTableView: (UITableView * tableView) {return 1;} - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) Sectio N {return self.viewModel.dataArray.count;} - (UITableViewCell *) tableView: (* UITableView) tableView cellForRowAtIndexPath: (NSIndexPath * indexPath) {LSCircleListTableCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithUTF8String:object_getClassName ([LSCircleListTableCell class])] forIndexPath:indexPath] if (self.viewModel.dataArray.count; > indexPath.row) {cell.viewModel = self.viewModel.dataArray[indexPath.row];}} return cell; #pragma mark UITableViewDelegate (CGFloat) (tableView: UITableView * tableView) heightForRowAtIndexPath: (NSIndexPath * indexPath) {return 100;} - (void) tableView: (UITableView * tableView) didSelectRowAtIndexPath: (NSIndexPath * indexPath) { If (self.viewModel.dataArray.count > indexPath.row) {[self.viewModel.cellClickSubject sendNext:self.viewModel.dataArray[indexPath.row]];}} - (UIView *) tableView: (* UITableView) tableView viewForHeaderInSection: (NSInteger) section {return self.sectionHeaderView;} - (CGFloat) tableView: (UITableView *) tableView heightForHeaderInSection: (NSInteger section) {return 45}; @end

The main View is divided into four modules:

I first module: system function

IOS MVVM+RAC from framework to combat

Each View will have the corresponding ViewModel, this is easier to reuse, because here is the main View, in general I will make VC and V share a VM, such as jump and data sharing has great benefits.

Second, a private function module:

IOS MVVM+RAC from framework to combat
IOS MVVM+RAC from framework to combat

The role has been marked on the road, it is necessary to pay attention to these different data processing, I wrote it myself, certainly not so logical, for reference only.

Third modules: lazy loading

IOS MVVM+RAC from framework to combat

There is nothing to say, is to use the MJRefresh third party library refresh. However, if you are careful, you will find that the following two View are configured to initialize the VM, which is the same as the main View configuration initialization meaning.

IV fourth modules: agent and data source

IOS MVVM+RAC from framework to combat

Which is used to customize the Cell, using ViewModel to configure, click the event is also linked to the jump before the VC, and the VM pass.

3, LSCircleListModel processing

Similarly, the first code

LSCircleListModel.h / / ZhongShui / / / / / / / / Created / / Copyright 16/3/17. by Wang Longshuai on of All rights reserved. in 2016. Wang Longshuai / / #import < Foundation/Foundation.h> @interface; LSCircleListModel: NSObject @property (nonatomic, copy) NSString *idStr @property (nonatomic, copy); NSString *title; @property (nonatomic, copy) NSString *intro @property (nonatomic, copy; *img @property (NSString); nonatomic, copy) NSString *memberCount @property (nonatomic, copy); NSString *topicCount; @end
LSCircleListModel.m / / ZhongShui / / / / / / / / Created / / Copyright 16/3/17. by Wang Longshuai on of All rights reserved. in 2016. Wang Longshuai / / #import "LSCircleListModel.h" @implementation + LSCircleListModel (NSDictionary * mj_replacedKeyFromPropertyName) {return @{@ @ "idStr": "Id", "title": @ @ "Title", "intro": @ @ "intro", "img": @ @ @ "img", "memberCount", "MemberCount": @ @ @ "topicCount": "TopicCount"};} @end

This map is not introduced, is a simple data model, using the MJExtention data model transformation framework. Did not do any other logical processing.

4, ViewModel processing

LSCircleListViewModel.h / / ZhongShui / / / / / / / / Created / / Copyright 16/3/10. by Wang Longshuai on of All rights reserved. in 2016. Wang Longshuai / / #import "YCViewModel.h" #import "LSCircleListHeaderViewModel.h" #import "LSCircleListSectionHeaderViewModel.h" @interface LSCircleListViewModel: YCViewModel @property (nonatomic, strong) RACSubject *refreshEndSubject @property (nonatomic, strong); RACSubject *refreshUI; @property (nonatomic, strong) RACCommand *refreshDataCommand; @ property (nonatomic, strong) RACCommand *nextPageCommand @property (nonatomic, strong); LSCircleListHeaderViewModel *listHeaderViewModel; @property (nonatomic, strong) LSCircleListSectionHeaderViewModel *sectionHeaderViewModel; @ property (nonatomic, strong) NS Array *dataArray; @property (nonatomic, strong) RACSubject *cellClickSubject; @end
LSCircleListViewModel.m / / ZhongShui / / / / / / / / Created / / Copyright 16/3/10. by Wang Longshuai on of All rights reserved. in 2016. Wang Longshuai / / #import "LSCircleListViewModel.h" #import "LSCircleListCollectionCellViewModel.h" #import "LSCircleListModel.h" @interface (LSCircleListViewModel) @property (nonatomic, assign) NSInteger currentPage; @end @implementation LSCircleListViewModel (void) yc_initialize {@weakify [self.refreshDataCommand.executionSignals.switchToLatest (self); subscribeNext:^ (NSDictionary *dict) {@strongify (self); if (dict = = Nil) {[self.refreshEndSubject sendNext:@ (LSRefreshError)]; ShowErrorStatus (@ "network connection failure"); return;} If ([dict[@ "status"] integerValue] = = 0) {self.listHeaderViewModel.dataArray = [[[([(NSDictionary) dict[@ "res" arrayForKey:@ "JoinCircles"]).Rac_sequence map:^id (NSDictionary *dic) {LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:dic] LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc]; init]; viewModel.model = model; return viewModel; array] mutableCopy]}]; [[[(self.dataArray = [(NSDictionary *) dict[@ "res" arrayForKey:@ "Circles"]).Rac_sequence map:^id (NSDictionary *dic) {LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:dic]; LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init]; viewModel.model = model; return viewModel;}] array] mutableCopy]; [self ls_setHeaderRefreshWithArray:dict[@ "Circles" [self "; ls_dismiss];} else {[self.refreshEndSubject sendNext:@ (LSRefreshError)]; ShowMessage (dict[@" MES "]);}}]; [[[self.refreshDataCommand.executing skip:1] take:1] subscribeNext:^ (ID x) {@strongify (self); if ([x isEqualToNumber:@ (YES)] [self) {ls_showWithStatus:@}]" loading ";}]; [self.nextPageCommand.executionSignals.switchToLa Test subscribeNext:^ (NSDictionary *dict) {@strongify (self); if (dict = = Nil) {[self.refreshEndSubject sendNext:@ (LSRefreshError)]; ShowErrorStatus (@ "network connection failure"); return;} if ([dict[@ "status"] integerValue] = = 0) {NSMutableArray *recommandArray = [[NSMutableArray alloc] initWithArray:self.dataArray]; for (NSDictionary *subDic [in (NSDictionary *) dict[@ "res" arrayForKey:@ "Circles"]) {LSCircleListModel *model = [LSCircleListModel mj_objectWithKeyValues:subDic]; LSCircleListCollectionCellViewModel *viewModel = [[LSCircleListCollectionCellViewModel alloc] init]; viewModel.model = model; [recommandArray addObject:viewModel];} self.dataArray = recommandArray; [self "Circles" ls_setFootRefreshWithArray:dict[@ "; [self ls_dismiss];} else {[self.refreshEndSubject sendNext:@ (LSRefreshError)]; ShowMessage (dict[@" MES "]);}}}]; #pragma - Private - mark (NSMutableDictionary *) requestCircleListWithId: (* NSString) idStr currentPage: (NSString * currentPage) {idStr = IF_NULL_TO_STRING (idStr); currentPage = IF_NULL_TO_STRING (currentPage); NSMutableDictionary * dict = [@{@" MemberID ":" pageSize ": idStr, @ LS_REQUEST_LIST_COUNT, @ pageIndex: currentPage} mutableCopy]; return dict;} - (void) ls_setFootRefre ShWithArray: (NSArray * array) {if (array.count < LS_REQUEST_LIST_NUM_COUNT) {[self.refreshEndSubject sendNext:@ (LSFooterRefresh_HasNoMoreData)];} else {[self.refreshEndSubject sendNext:@ (LSFooterRefresh_HasMoreData)];}} - (void) ls_setHeaderRefreshWithArray: (NSArray * array) {if (array.count < LS_REQUEST_LIST_NUM_COUNT [self.refreshEndSubject sendNext:@ (LSHeaderRefresh_HasNoMoreData)) {else {[self.refreshEndSubject}]; sendNext:@ (LSHeaderRefresh_HasMoreData)];}} - #pragma mark - lazyLoad (RACSubject * refreshUI) {if (_refreshUI! = [RACSubject) {_refreshUI subject]}; return _refreshUI;} - (RACSubject * refreshEndSubject) {if (_refr! EshEndSubject = [RACSubject subject]) {_refreshEndSubject}; return _refreshEndSubject;} - (RACCommand * refreshDataCommand) {if (! _refreshDataCommand) {@weakify (self); _refreshDataCommand = [[RACCommand alloc] initWithSignalBlock *:^RACSignal (ID input) {@strongify (self); return [RACSignal (createSignal:^RACDisposable * id< RACSubscriber> subscriber) {@strongify (self); self.currentPage = 1; [self.request POST:LS_URL_CIRCLE_MEMBER_LIST parameters:[self requestCircleListWithId:@ "1" currentPage:[NSString stringWithFormat:@ "%d" self.currentPage]] success:^ (CMRequest *request, NSString *responseString) { NSDictionary *dict = [responseString objectFromJSONString]; [subscriber sendNext:dict]; [subscriber sendCompleted];} failure:^ (CMRequest *request, NSError *error) {ShowErrorStatus (@ "network connection failure"); [subscriber sendCompleted];}]; return nil;}]}]; return _refreshDataCommand;};} - (RACCommand * nextPageCommand) {if ({@weakify (_nextPageCommand!) self; _nextPageCommand = [[RACCommand) alloc] * initWithSignalBlock:^RACSignal (ID input) {@ strongify (self); return [RACSignal (createSignal:^RACDisposable * id< RACSubscriber> Subscriber) {@strongify (self); self.currentPage [self.request POST:LS_URL_CIRCLE_TOPIC_LIST parameters:nil (success:^ + +; CMRequest *request, NSString *responseString) {NSDictionary *dict = [responseString objectFromJSONString]; [subscriber sendCompleted]; sendNext:dict] subscriber;} failure:^ (CMRequest *request, NSError *error) {@strongify (self; self.currentPage) - ShowErrorStatus (@; "network connection failure"); [subscriber sendCompleted]}]; return; nil;}]}]; return _nex;} TPageCommand;} - (LSCircleListHeaderViewModel * listHeaderViewModel) {if (_listHeaderViewModel! = [[LSCircleListHeaderViewModel) {_listHeaderViewModel alloc] init]; _listHeaderViewModel.title = @ "has joined the circle"; _listHeaderViewModel.cellClickSubject = self.cellClickSubject;} return _listHeaderViewModel;} - (LSCircleListSectionHeaderViewModel * sectionHeaderViewModel) {if (! _sectionHeaderViewModel) {_sectionHeaderViewModel = "LSCircleListSectionHeaderViewModel alloc] init]; _sectionHeaderViewModel.title = @" circle ";} return _sectionHeaderViewModel;} - (NSArray * dataArray) {if (_dataArray! = [[NSArray) {_dataArray alloc] init]}; Return _dataArray;} (RACSubject *) cellClickSubject {if ((_cellClickSubject) {_cellClickSubject = [RACSubject subject];} return _cellClickSubject;})

ViewModel is also divided into three modules, because the code is too important to pick

I first module: data processing, logic module

IOS MVVM+RAC from framework to combat

Processing of this data, the first to use a dictionary to Model, in the configuration of the ViewModel Model, ViewModel and then to UI and its logical correspondence.

Second, a private function module:

IOS MVVM+RAC from framework to combat

For the request parameter dictionary, can be placed in the VM, to facilitate modular transplantation, can also be placed in the public API easy to manage, see the individual choice, there is no absolute good location, only more suitable for personal location.

The other two functions deal with private functions that have no more data when they are pulled and pulled.

Third modules: lazy loading

IOS MVVM+RAC from framework to combat

This data request is AFNetworking.

5, APPDelegate code simplification

In general, our formal project will encounter a lot of need to start the project when it is loaded, so APPDelegate will soon become large, since the other code simplified decoupling, here also can do some processing.

The contents are as follows:

IOS MVVM+RAC from framework to combat

The simplified AppDelegate is as follows:

IOS MVVM+RAC from framework to combat

Other code storage locations are as follows:

IOS MVVM+RAC from framework to combat

When the class object is the introduction of the project, runtime will be sent to each class object load message. Load method is very magical, because it will be in every class and classification are introduced when only one call, call the order of priority in the superclass subclass subclass and prior to classification. The load method will not be automatic inheritance, every load in the class do not need the same method call the parent class like viewDidLoad.

This is the use of this is a black magic thing, ha ha, it simplifies the APPDelegate!

Four, postscript

Originally wanted to kill the base class, want to use Category + Protocol and Methode using Runtime Swizzle to give the system function to add a private function of their own, the original VC has a fix, but found that it involves too broad, you do a Category on VC, UINavigationController will also be affected, if you have a Category of View. The other successor View will also have an impact, and then exchange methods are working in a Category, to second on the cover… Do not create why, because I know this road is not going to continue to get out of the…

Here, we should have some understanding of my pen framework, because it relates to what is really too much, are these things need to stand on the shoulders of giants, when there is no mention but do not know how to:

IOS MVVM+RAC from framework to combat

Ha ha ha! Don’t blame me not… I am not responsible for, because you can see here the length is beyond ordinary people can accept, and I feel I have to take care of all the details of it (on for grinding goblins)! We have any doubts we can exchange in the comments area!

Finally, I really hope that the great God pointed out that the lack of place, so that we can make progress together!

Five, Demo has been out (move your fingers, like Caesar!)

Code address:

In this paper, the author of a book written by Wang Longshuai original, reproduced please retain copyright. Thanks, and share your understanding, let life become more beautiful! (a little frighten people, said: the reprint please inform the author of legal liability, otherwise the consequences!)