iOS MVP Architecture Pattern

Get into the habit of writing together! This is the 9th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

MVPProtocol Oriented Programming, ie Model View Presenter(Model View Coordinator).

feature

  • Amortization of tasks: Divide the most important tasks into Presenterand Modelwith Viewless functions
  • Testability: Due to a functionally simple Viewlayer, testing business logic becomes simple
  • Ease of use: More code than using MVCpatterns, but MVPpatterns have a very clear structure

So, MVCthe MVPdifference with and is that there is no direct communication between MVPneutral Modeland .View

Design Patterns

  • ModelThe layer is not just to create a data object, it should also contain network requests, and data Cachemanipulation
  • ViewLayers are some encapsulation, reuse
  • PresenterThe layer does not involve network requests and operations of data , but is just a bridge to Cachebuild a Modellayer and a layerView

Advantage

  • The model is completely separated from the view, modify the view without affecting the model
  • Models can be used more efficiently because all interactions are Presenterin
  • Put logic in Presenter, you can unit test away from the user interface
  • You can use one Presenerfor multiple views without changing Presenterthe logic. This feature is very useful because the view always changes more frequently than the model

UIwith Modeldecoupling

Create LGProtocoland implement Cellthe event interface of the button click in the middle

#import <Foundation/Foundation.h>

@protocol LGProtocol <NSObject>

- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;

@end
复制代码

In , the method Presentthat implements LGProtocolthe protocoldidClickNum

#import <Foundation/Foundation.h>
#import "Model.h"
#import "LGProtocol.h"
#import <YYKit.h>

@interface Present : NSObject<LGProtocol>

@property (nonatomic, strong) NSMutableArray    *dataArray;
@property (nonatomic, weak) id<LGProtocol>       delegate;

@end

@implementation Present

- (instancetype)init{
    if (self = [super init]) {
        [self loadData];
    }
    return self;
}


- (void)loadData{
    
    NSArray *temArray =
    @[
      @{@"name":@"HK",@"imageUrl":@"http://HK",@"num":@"99"},
      @{@"name":@"KD",@"imageUrl":@"http://KD",@"num":@"99"},
      @{@"name":@"CC",@"imageUrl":@"http://CC",@"num":@"99"},
      @{@"name":@"KC",@"imageUrl":@"http://KC",@"num":@"59"},
      @{@"name":@"LA",@"imageUrl":@"http://LA",@"num":@"24"}];

    for (int i = 0; i<temArray.count; i++) {
        Model *m = [Model modelWithDictionary:temArray[i]];
        [self.dataArray addObject:m];
    }
}

#pragma mark -LGProtocol
- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath{
    
    @synchronized (self) {
        if (indexpath.row < self.dataArray.count) {
            
            Model *model = self.dataArray[indexpath.row];
            model.num    = num;
        }
    }
}

#pragma mark - lazy
- (NSMutableArray *)dataArray{
    if (!_dataArray) {
        _dataArray = [NSMutableArray arrayWithCapacity:10];
    }
    return _dataArray;
}

@end
复制代码

In MVCTableViewCell, remove UIthe Modeldata binding for the pair. In the interactive method of the button click, the delegatemethod didClickNumcalled

- (void)didClickAddBtn:(UIButton *)sender{
    self.num++;
}

- (void)setNum:(int)num{
    _num                = num;
    self.numLabel.text  = [NSString stringWithFormat:@"%d",self.num];

    // 发出响应 model delegate UI 
    if (self.delegate && [self.delegate respondsToSelector:@selector(didClickNum:indexpath:)]) {
        [self.delegate didClickNum:self.numLabel.text indexpath:self.indexPath];
    }
}

// 删除UI对Model的数据绑定
//- (void)setModel:(Model *)model{
//    _model              = model;
//    self.numLabel.text  = model.num;
//    self.nameLabel.text = model.name;
//}
复制代码

Come to the VCcode , complete the data binding of the middle pair, LMDataSourceand the setting of theBlockCellUIModeldelegateindexPath

#import "MVCViewController.h"
#import "LMDataSource.h"
#import "MVCTableViewCell.h"
#import "Present.h"
#import "Model.h"
#import "LGView.h"

@interface MVCViewController ()

@property (nonatomic, strong) LGView            *lgView;
@property (nonatomic, strong) LMDataSource      *dataSource;
@property (nonatomic, strong) Present           *pt;

@end

@implementation MVCViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 建立关系
    self.view = self.lgView;
    [self.dataSource setDataArray:self.pt.dataArray];
    [self.lgView setDataSource:self.dataSource];
}

#pragma mark - lazy
// UI布局代码
- (LGView *)lgView{
    
    if(!_lgView){
        _lgView = [[LGView alloc] initWithFrame:self.view.bounds];
    }
    
    return _lgView;
}

// 数据提供层
- (Present *)pt{
    
    if(!_pt){
        _pt = [[Present alloc] init];
    }
    
    return _pt;
}

// 数据代理层
- (LMDataSource *)dataSource{
    
    if(!_dataSource){
        __weak typeof(self) weakSelf = self;
        _dataSource = [[LMDataSource alloc] initWithIdentifier:[MVCTableViewCell reuserId] configureBlock:^(MVCTableViewCell *cell, Model *model, NSIndexPath *indexPath) {
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            cell.numLabel.text  = model.num;
            cell.nameLabel.text = model.name;
            cell.delegate       = strongSelf.pt;
            cell.indexPath      = indexPath;
        }];
    }
    
    return _dataSource;
}

@end
复制代码

two-way binding

When the following requirements are encountered during development, two-way binding between UI and Model is required. CellFor example: the triggered didClickAddBtnevent, when num++greater than 5, the UIlayer only displays the first two data

Modify LGProtocol, add UIrefresh event interface

#import <Foundation/Foundation.h>

@protocol LGProtocol <NSObject>

- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath;

- (void)reloadUI;

@end
复制代码

PresentThe method in the modification didClickNum, increase numgreater than 5, keep only the first two data, and call delegatethe reloadUImethod to refreshUI

- (void)didClickNum:(NSString *)num indexpath:(NSIndexPath *)indexpath{
    
    @synchronized (self) {
        if (indexpath.row < self.dataArray.count) {
            
            Model *model = self.dataArray[indexpath.row];
            model.num    = num;

            if ([num intValue] > 5) {

                [self.dataArray removeAllObjects];

                NSArray *temArray =
                @[
                  @{@"name":@"HK",@"imageUrl":@"http://HK",@"num":@"99"},
                  @{@"name":@"KC",@"imageUrl":@"http://KC",@"num":@"99"}];

                for (int i = 0; i<temArray.count; i++) {
                    Model *m = [Model modelWithDictionary:temArray[i]];
                    [self.dataArray addObject:m];
                }

                if (self.delegate && [self.delegate respondsToSelector:@selector(reloadUI)]) {
                    [self.delegate reloadUI];
                }
            }
        }
    }
}
复制代码

Modify LGView, implement LGProtocolthe refresh UImethod of the protocol

#import <UIKit/UIKit.h>
#import "MVCTableViewCell.h"
#import "LGProtocol.h"

@interface LGView : UIView<LGProtocol>

@property (nonatomic, strong) UITableView       *tableView;

- (void)setDataSource:(id<UITableViewDataSource>)dataSource;

@end

@implementation LGView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        [self addSubview:self.tableView];
    }
    return self;
}

- (void)setDataSource:(id<UITableViewDataSource>)dataSource{
    self.tableView.dataSource = dataSource;
}

- (void)reloadUI{
    [self.tableView reloadData];
}

- (UITableView *)tableView{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
        _tableView.tableFooterView = [UIView new];
        _tableView.backgroundColor = [UIColor whiteColor];
        [_tableView registerClass:[MVCTableViewCell class] forCellReuseIdentifier:[MVCTableViewCell reuserId]];
    }
    return _tableView;
}

@end
复制代码

Modify MVCViewController, will UIand Modelbuild relationship

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 建立关系
    self.view = self.lgView;
    [self.dataSource setDataArray:self.pt.dataArray];
    [self.lgView setDataSource:self.dataSource];
    //将UI和Model建立关系
    self.pt.delegate = self.lgView;
}
复制代码

For the above MVPsimple case, there are still many flaws. This kind of rudimentary code is difficult to meet even the common needs in development

For example the following situations:

  • encounter more complex Cellforms
  • Using too much iso- delegateglue code
  • When the module level is deeper, it is difficult to communicate with each other

Therefore, in response to the above problems, but also to introduce MVPthe real way of use.

Guess you like

Origin juejin.im/post/7086831020703645710