美文网首页架构设计模式
iOS 项目中对 MVVM 实践

iOS 项目中对 MVVM 实践

作者: 人魔七七 | 来源:发表于2018-12-10 17:39 被阅读127次

    前言

    针对最近研究的一些知识点的实战,抽出一个模块对项目进行优化升级。

    架构注意的两点

    职责界限划分以及模块之间的通信

    1. 职责界限划分
    • View 展示数据,并且会持有用户的点击事件,里面包含一些布局或者绘制的东西。
    • Controller根据用户点击事件协调调用ViewModel里处理好的数据。观察ViewModel的变化并通知View更新。
    • ViewModel 里面持有了UI有关的数据(这些数据是从服务器或者缓存获得经过处理的数据),以及获取数据的一些方法。
    • 有时候为了避免ViewModel臃肿,把数据获取相关的逻辑(有可能是服务器上新的数据,也有可能是本地的数据)分离到一个新的模块来处理暂且叫Repository模块。
    • Model 服务器返回的JSON对应的数据。
    1. 模块之间的通信

    上层的组件持有下层组件的引用,下层组件不可以拥有上层组件的引用。
    但是可以通过callback来处理,比如控制器观察ViewModel的方式通知控制器数据的变化,以便于控制器告诉View要update数据。

    各模块的大致代码结构

    Model

    大致流程是从JSON映射到Model,Model属性可能需要我们自己重命名(可能服务器满足不了我们),Model最好能满足持久化需求,比如我要把某个Model存文件来缓存提供下一次使用。

    ViewModel

    • 这个类主要相应View传来的事件获取数据,(数据可能从服务器获取,也有可能从本地缓存获取,里面缓存策略,以及过期的处理还需要我们处理,通常还要和后台人员来商量)。

    • 还有一些对应界面的data以及界面需要的必要的参数

    • 还有我们数据处理中的一些状态,比如成功,失败等,这里是专门弄了一个类来处理。

    NS_ASSUME_NONNULL_BEGIN
    
    @interface WCSDurableGoodsRecipientsListViewModel : NSObject
    
    #pragma mark - In 请求服务器或者本地缓存需要的参数
    
    @property (copy, nonatomic) NSString *accountId;
    @property (copy, nonatomic) NSString *searchTime;
    @property (assign, nonatomic) NSInteger start;
    
    #pragma mark - Out 界面上展示的数据
    
    @property (copy, nonatomic) NSString *totalAppleyeStr;
    @property (copy, nonatomic) NSString *awaitStr;
    @property (strong, nonatomic) NSMutableArray *tableViewDataArray;
    
    #pragma mark - NetWorkStatus 网络的状态
    
    @property (strong, nonatomic) NetWorkResults *netWorkResults;
    
    
    #pragma mark - Out 界面用户的事件
    
    - (void)refresh;
    - (void)loadMore;
    
    @end
    

    一个处理数据反馈状态的类

    typedef NS_ENUM(NSInteger, NetWorkResultStatus) {
        NetWorkResultStatusSuccess = 0,
        NetWorkResultStatusError = 1,
        NetWorkResultStatusLoading = 2,
        
    };
    @interface NetWorkResults : NSObject
    
    @property (assign,readonly) NSInteger status;
    @property (nonatomic,readonly) id content;
    - (instancetype)initWithStatus:(NSInteger)status content:(id)content;
    
    @end
    

    Controller

    这里主要是我们调用ViewModel的方法,以及监听ViewModel的界面数据的变化来更新我们的View界面。这里是观察数据代码

    #pragma mark - KVO
    
    - (void)wcs_handleViewModelUpdate
    {
        [self.KVOController observe:self.durableGoodsRecipientsListViewModel keyPath:FBKVOKeyPath(self.durableGoodsRecipientsListViewModel.tableViewDataArray) options:NSKeyValueObservingOptionInitial| NSKeyValueObservingOptionNew block:^(id observer, id object, NSDictionary *change) {
            
            id newValue = change[NSKeyValueChangeNewKey];
            self.dataSourceArray = [(NSMutableArray *)newValue copy];
            [self.tableView reloadData];
           
        }];
        [self.KVOController observe:self.durableGoodsRecipientsListViewModel keyPath:FBKVOKeyPath(self.durableGoodsRecipientsListViewModel.netWorkResults) options:NSKeyValueObservingOptionInitial| NSKeyValueObservingOptionNew block:^(id observer, id object, NSDictionary *change) {
            
            id newValue = change[NSKeyValueChangeNewKey];
            if ([newValue isKindOfClass:[NetWorkResults class]])
            {
                NetWorkResults *result = (NetWorkResults *)newValue;
                if (result.status == NetWorkResultStatusError)
                {
                    [self showHUDWithTitle:@"" subTitle:result.content];
                }
            }
            
        }];
    }
    

    注意:

    • 我们用的FB的一个KVOController框架来观察ViewModel的数据变化以便update UI。这里标明RAC这个框架并不是MVVM必须的东西,其实它也是基于KVO封装的。
    • 为了防止我们把keyPath写错可以用一个宏来处理如下。
    #define FBKVOKeyPath(KEYPATH) \
    @(((void)(NO && ((void)KEYPATH, NO)), \
    ({ const char *fbkvokeypath = strchr(#KEYPATH, '.'); NSCAssert(fbkvokeypath, @"Provided key path is invalid."); fbkvokeypath + 1; })))
    
    #define FBKVOClassKeyPath(CLASS, KEYPATH) \
    @(((void)(NO && ((void)((CLASS *)(nil)).KEYPATH, NO)), #KEYPATH))
    

    对于一些特殊的子View处理比如Cell

    可以针对cell创建一个ViewModel,在TableViewModel中处理下。
    - (NSArray *)viewModelsDurableGoodsRecipients:(WCSDurableGoodsRecipientsListModelV2 *)applyrecords
    {
        NSMutableArray *viewModels = [NSMutableArray arrayWithCapacity:applyrecords.body.applyRecords.count];
        
        for (WCSDurableGoodsRecipientsListApplyrecordsModelV2 *applyrecordsModelV2 in applyrecords.body.applyRecords) {
            WCSDurableGoodsRecipientsListCellV2ViewModel *goodsRecipientsListCellV2ViewModel = [[WCSDurableGoodsRecipientsListCellV2ViewModel alloc] initWithGoods:applyrecordsModelV2];
            [viewModels addObject:goodsRecipientsListCellV2ViewModel];
        }
       
        
        return viewModels.copy;
    }
    
    
    cell中的处理
    - (void)bindViewModel:(WCSDurableGoodsRecipientsListCellV2ViewModel *)viewModel
    {
    //    NSString *textStr = viewModel.applyrecordsModelV2.applyDesc;
    }
    
    控制器里的处理
    - (void)configureCell:(WCSDurableGoodsRecipientsListCellV2 *)cell withObject:(WCSDurableGoodsRecipientsListCellV2ViewModel *)object
    {
        [cell bindViewModel:object];
    }
    

    架构图

    架构图

    相关文章

      网友评论

        本文标题:iOS 项目中对 MVVM 实践

        本文链接:https://www.haomeiwen.com/subject/drsdhqtx.html