美文网首页架构设计
iOS 架构设计实践:从 MVVM 到 VIPER

iOS 架构设计实践:从 MVVM 到 VIPER

作者: 黄花菜先生 | 来源:发表于2025-08-26 17:32 被阅读0次

在 iOS 开发中,随着应用规模增大和业务复杂度提升,单纯的 MVC 很容易导致 ViewController 臃肿、难以维护。本文通过示例,系统讲解 MVVMVIPER 架构,并分析如何选择合适架构。

一、MVVM vs MVC

  • MVC 的问题

    • Controller 承担了 UI 展示 + 业务逻辑 + 数据处理 + 网络请求

    • 随着项目复杂度增加,Controller 越来越臃肿(Massive ViewController 问题)

  • MVVM 的改进

    • ViewModel 负责 数据加工 + 状态管理 + 业务逻辑

    • View/Controller 只负责 UI 展示和用户交互

优点

  • Controller 变轻

  • 数据和 UI 分离

二、MVVM 架构示例

Model

@interface EVUser : NSObject
@property (nonatomic, copy) NSString *userID;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger gender; // 0 女,1 男
@end

@interface EVUserDisplayItem : NSObject
@property (nonatomic, copy) NSString *userID;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *genderString;
@end

ViewModel

@interface EVUserListViewModel : NSObject
@property (nonatomic, copy) NSArray<EVUserDisplayItem *> *displayItems;
- (void)loadUsers;
@end

@implementation EVUserListViewModel
- (void)loadUsers {
    EVUser *user1 = [EVUser new]; user1.userID = @"1"; user1.name = @"Evan"; user1.gender = 1;
    EVUser *user2 = [EVUser new]; user2.userID = @"2"; user2.name = @"Alice"; user2.gender = 0;

    NSMutableArray *items = [NSMutableArray array];
    for (EVUser *user in @[user1, user2]) {
        EVUserDisplayItem *item = [EVUserDisplayItem new];
        item.userID = user.userID;
        item.name = user.name;
        item.genderString = (user.gender == 0) ? @"女" : @"男";
        [items addObject:item];
    }
    self.displayItems = items;
}
@end

三、VIPER 对 MVVM 的进一步解耦

MVVM 已经把 Controller 和数据逻辑分开,但:

  • ViewModel 可能还直接依赖 Service/网络请求

  • 模块之间耦合仍存在

VIPER 做法

  1. 通过协议完全解耦模块:View ↔ Presenter ↔ Interactor ↔ Router

  2. 职责更细化:

    • Interactor:业务逻辑、网络请求

    • Presenter:把业务数据转换成 UI 可展示格式

    • Router:页面跳转 / 对外接口

    • View:纯 UI 展示

一句话总结

  • MVVM → 解决 Controller 臃肿问题,让数据和 UI 分离

  • VIPER → 在 MVVM 基础上,把各模块完全解耦,明确职责,并通过 Router 暴露统一入口

四、VIPER 核心示例

Entity

@interface EVVIPERUser : NSObject
@property (nonatomic, copy) NSString *userID;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger gender;
@end

@interface EVVIPERUserDisplayItem : NSObject
@property (nonatomic, copy) NSString *userID;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *genderString;
@end

Interactor

@protocol EVVIPERUserListInteractorProtocol <NSObject>
- (void)fetchUsersWithCompletion:(void(^)(NSArray<EVVIPERUser *> *users))completion;
@end

@interface EVVIPERUserListInteractor : NSObject<EVVIPERUserListInteractorProtocol>
@end

@implementation EVVIPERUserListInteractor
- (void)fetchUsersWithCompletion:(void(^)(NSArray<EVVIPERUser *> *users))completion {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        EVVIPERUser *user1 = [EVVIPERUser new]; user1.userID = @"1"; user1.name = @"Evan"; user1.gender = 1;
        EVVIPERUser *user2 = [EVVIPERUser new]; user2.userID = @"2"; user2.name = @"Alice"; user2.gender = 0;
        if (completion) completion(@[user1, user2]);
    });
}
@end

Presenter

@protocol EVVIPERUserListViewProtocol <NSObject>
- (void)updateWithDisplayItems:(NSArray<EVVIPERUserDisplayItem *> *)items;
@end

@protocol EVVIPERUserListPresenterProtocol <NSObject>
- (void)loadUsers;
@end

@interface EVVIPERUserListPresenter : NSObject <EVVIPERUserListPresenterProtocol>
@property (nonatomic, weak) id<EVVIPERUserListViewProtocol> view;
@property (nonatomic, strong) id<EVVIPERUserListInteractorProtocol> interactor;
@property (nonatomic, strong) id router;
@end

@implementation EVVIPERUserListPresenter
- (void)loadUsers {
    [self.interactor fetchUsersWithCompletion:^(NSArray<EVVIPERUser *> *users) {
        NSMutableArray *items = [NSMutableArray array];
        for (EVVIPERUser *user in users) {
            EVVIPERUserDisplayItem *item = [EVVIPERUserDisplayItem new];
            item.userID = user.userID;
            item.name = user.name;
            item.genderString = (user.gender == 0) ? @"女" : @"男";
            [items addObject:item];
        }
        [self.view updateWithDisplayItems:items];
    }];
}
@end

五、MVVM 与 VIPER 对比

特性 MVVM VIPER
关注点 数据驱动、界面更新 模块化、业务解耦
文件数量 较少 较多
测试难度 中等 中等
适用场景 小中型项目 中大型项目,复杂业务逻辑 / SDK
学习成本
数据处理 ViewModel Interactor + Presenter
事件处理 Controller View + Presenter
模块替换性

六、补充实践点

  1. Service 层:处理网络请求、接口封装、模拟数据等,不影响 ViewModel 逻辑

  2. 异步请求:ViewModel 调用 Service,获取数据后回调更新 View

  3. 点击事件 / 埋点:一般在 View 或 Presenter 处理,保持 ViewModel 纯数据处理

Demo 地址:https://github.com/EvanCaiDev/iOSArchSeriesDemo

相关文章

网友评论

    本文标题:iOS 架构设计实践:从 MVVM 到 VIPER

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