美文网首页
【设计模式】09 - MVVM模式 (Model-View-Vi

【设计模式】09 - MVVM模式 (Model-View-Vi

作者: Lebron_James | 来源:发表于2019-05-06 19:21 被阅读0次

这篇文章是我阅读raywenderlich.comDesign Patterns by Tutorials的总结,文中的代码是我阅读书本之后根据自己的想法修改的。如果想看原版书籍,请点击链接购买。


MVVM是一个结构设计模式,主要有三部分组成:

  • Model:保存应用的数据,通常是struct或者class。
  • View:显示应用界面,通常继承自UIView
  • View Model:把Model的数据转化成可以显示在View上的数据,通常是class类型。

什么时候使用

当我们需要把Model的数据转化成可以显示在View的时候使用。例如,把Date转化成以String的形式显示。如果我们使用MVC的话,就需要把转化的代码写到View Controller,造成View Controller的代码太多,就成了我们所说的Massive View Controller。

简单Demo

我们假设要显示一个当前用户正在关注的用户列表,使用MVVM模式。Model是User;View Model是UserListViewModelUserListViewController负责管理View。

User

struct UsersResults: Codable {
    let users: [User]
}

struct User: Codable {
    var userID: String?
    var username: String?
}

User遵循CodableUsersResults用于辅助解析服务器返回用户数据,同样遵循Codable。关于Codable,如果不懂的,请大家自行去学习了解。

UserListViewModel

final class UserListViewModel {

    private var users: [User] = []

    // MARK: - Data

    func numberOfRows() -> Int {
        return users.count
    }

    func user(at index: Int) -> User? {
        guard index < users.count else { return nil }
        return users[index]
    }

    // MARK: - Fetching

    private let decoder = JSONDecoder()

    func fetchFollowings(userID: String, completion: (() -> Void)? = nil) {
        let url = "https://api.your_app_name.com/api/v1/users/\(userID)/followings"
        RequestManager.request(.get, urlString: url) { [weak self] (result) in
            switch result {
            case .success(let data):

                guard let jsonData = data,
                    let result = try? self?.decoder.decode(UsersResults.self, from: jsonData),
                    let users = result.users else {
                        self?.users = []
                        return
                }
                self?.users = users

            case .failure(let error):
                self?.users = []
            }
        }
    }
}

我们上面讲到,View Model是要把Model的数据转化成可以显示在View上的数据,所以View想要什么数据,View Model就准备什么数据。

首先要从服务器获取数据,所以写了一个fetchFollowings方法。RequestManager是我自己封装的,request方法的最后一个参数是closure,请求完成后调用,closure里包含一个枚举类型的result: 1) .success(Data?): 如果请求成功,并且有数据,那我们解析成[User];2) .fail(Error?): 请求失败。

要显示用户列表,通常用一个UITableView,我们需要告诉UITableView有多少行,每一行对应的User,所以就有了numberOfRows()user(at index: Int)方法。

UserListViewController

final class UserListViewController: UIViewController {

    private let viewModel: UserListViewModel

    // MARK: - Initializers

    init(withViewModel vm: UserListViewModel) {
        viewModel = vm
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - Views

    private lazy var tableView: UITableView = {
        let tv = UITableView(frame: .zero)
        tv.dataSource = self
        tv.register(UITableViewCell.self, forCellReuseIdentifier: "UserCell")
        return tv
    }()

    // MARK: - View LifeCycles

    override func loadView() {
        view = UIView()
        view.backgroundColor = .white
        addTableView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let userID = "xxxxxxxxxxxxxx"
        viewModel.fetchFollowings(userID: userID) { [weak self] in
            self?.tableView.reloadData()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

// MARK: - UITableViewDataSource
extension UserListViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int {
        return viewModel.numberOfRows()
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath)
        let user = viewModel.user(at: indexPath.row)
        cell.textLabel?.text = user?.username
        return cell
    }
}

// MARK: - Setup Subviews
extension UserListViewController {
    private func addTableView() {
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
            ])
    }
}

在MVVM模式中,我们会让View Controller持有一个View Model,通过初始化函数完成初始化;在loadView()添加UITableView;在实现UITableViewDataSource中,我们直接通过View Model来告诉tableview所需要的数据;在viewDidLoad中,通过View Model获取数据,请求完成后,刷新tableview。

总结

使用MVVM模式,我们把大量的代码转移到了View Model,大大减轻了View Controller的负担。View Controller需要什么数据,就让View Model告诉它。非常适合在比较大型的应用中使用。如果是第一次学习应用开发,建议还是从MVC开始。

欢迎加入我管理的Swift开发群:536353151

下一篇文章:【设计模式】10 - 工厂模式 (Factory Pattern)

相关文章

  • 【设计模式】09 - MVVM模式 (Model-View-Vi

    这篇文章是我阅读raywenderlich.com的Design Patterns by Tutorials的总结...

  • Vue快速入门

    MVVM模式 什么是MVVM模式? MVVM(Model-View-ViewModel)是一种软件架构设计模式,由...

  • vue面试问题

    1.MVVM模式和MVC模式 MVVM模式是Model-View-ViewModel的缩写。MVVM是一种设计...

  • 关于 MVVM 设计模式

    本文主要浅析传统 MVP 设计模式与先进流行的 MVVM 设计模式的一些区别,以及简要分析 MVVM 设计模式的优...

  • MVVM设计模式

    MVVM设计模式 在介绍MVVM设计模式之前我们先介绍一下DataBinding DataBinding,2015...

  • 设计模式演变过程

    基本设计模式之MVC模式 基本设计模式之MVP模式 基本设计模式之MVVM模式 SPA和MPA SPA:单页面应用...

  • 装饰者模式——IO流运用

    推荐博客Java设计模式学习09Java设计模式学习09Java设计模式学习09 装饰者模式还是比较难懂的。。。。...

  • iOS 设计模式 一

    设计模式随记 系统架构模式 1. MVC - MVVM - MVP - MVVM、MVC协调版 MVC :...

  • 了解 MVVM 以及为什么它实际上是有意义的!

    什么是设计模式? 好吧,在讨论 MVVM 或其他设计模式之前。首先,为什么我们需要这些设计模式,什么是设计模式? ...

  • 浅谈iOS设计模式

    序言:设计模式是一种编码经验,设计模式有MVC模式,工厂模式,单例模式,委托模式,MVVM模式,观察者模式。iOS...

网友评论

      本文标题:【设计模式】09 - MVVM模式 (Model-View-Vi

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