发布于 2026-01-06 0 阅读
0

Swift/iOS 中的 MVVM 模式示例

Swift/iOS 中的 MVVM 模式示例

随着面向事件的应用程序越来越多,MVVM 模式也越来越受欢迎。在 iOS 开发中,使用 MVVM 相较于传统的 MVC 模式有很多优势,首先就是将业务逻辑与表示层完全分离。我们不再需要使用一个“庞大的 ViewController”来处理过多任务,而是可以将诸如从网络或本地数据库请求数据之类的操作委托给另一个实体。

https://thepracticaldev.s3.amazonaws.com/i/ibcak32czzov3pwvyskp.png

所有这些应用程序逻辑都位于 ViewModel 中,ViewModel 本身并不知道视图是什么,也不知道视图的功能是什么。这种架构使得它非常易于测试,并最大限度地降低了视图的复杂性,使其尽可能地简单。

视图拥有 ViewModel,并始终“监听”视图的更改以更新 UI。这就是著名的“双向数据绑定”:如果视图发生更改,模型也会更新;反之亦然,而 ViewModel 始终作为两者之间的中介。

那么,如果我们在实践中看到这种情况会怎样呢?想象一下以下场景:我想开发一个应用程序,它可以连接到 GitHub 的 REST API,让我能够在代码仓库中搜索、选择一个仓库,并查看其最新的提交记录。让我们开始吧!

我将从搜索功能开始,为了简洁起见,我会忽略很多内容,但你可以在最后看到所有的源代码。

让我们编写满足 ViewModel 要求的协议:

protocol SearchRepositoriesDelegate {
    func searchResultsDidChanged()
}

protocol SearchViewModelType {
    var results: [SearchResult] {get}

    var query: String {get set}

    var delegate: SearchRepositoriesDelegate? {get set }
}
Enter fullscreen mode Exit fullscreen mode

一个用于存储结果的属性,一个用于存储检索这些结果的查询的属性,以及一个用于通知视图已发生更改的委托。很简单,对吧?

请注意,在另一种情况下,“SearchRepositoriesDelegate”应该被某种数据绑定机制或可观察对象所取代,但这将在另一篇文章中讨论。

那么我们的 ViewModel 将按以下方式工作:

1. 视图会引用 ViewModel。2
. 当用户在搜索栏中输入内容时,视图会根据搜索查询更新 ViewModel 的“query”属性。3
. 每次“query”属性更新时,ViewModel 都会向 GitHub API REST API 发送请求并更新搜索结果。4
. 一旦收到服务器响应,ViewModel 会通过代理通知视图搜索结果已更改。5
. 每次调用“searchResultsDidChanged”函数时,视图都会更新 UI。

让我们来看看具体实现情况:

视图模型:

    class SearchViewModel : SearchViewModelType {
    var delegate: SearchRepositoriesDelegate?

    var results : [SearchResult] = [] {//0 results by default
        didSet{
            delegate?.searchResultsDidChanged() //notify
        }
    }

    var searchService: SearchService

    var query: String = "" {
        didSet {
            if query == "" {
                results = []
            }else {
                performSearch()
            }
        }
    }

    init(service: SearchService) {
        self.searchService = service
    }

    private func performSearch() {
        searchService.search(query: self.query)
            .onSuccess { results in
                self.results = results
            }.onFailure { error in
                //do nothing
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

看法:

class SearchViewController: UIViewController {

    ...

    var searchViewModel: SearchViewModelType!

    override func viewDidLoad() {
        super.viewDidLoad()

        searchViewModel.delegate = self

        ...
    }
}

extension SearchViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        searchViewModel.query = searchText
    }
}

extension SearchViewController: SearchRepositoriesDelegate {
    func searchResultsDidChanged() {
        self.tableView.reloadData()
    }
}

...
Enter fullscreen mode Exit fullscreen mode

当用户在搜索栏中输入内容时,属性“query”会被更新,ViewModel 会向服务器端发出请求。请求完成后,它会通知视图,视图会调用 UITableView 的“reloadData()”函数来更新 UI。正如您所看到的,SearchViewModel 类非常容易测试,因为我们只需要创建一个模拟的“SearchService”对象来检查它是否正常工作。ViewModel 不引用视图,视图也不受任何业务逻辑的影响。

就这些!欢迎点击链接查看所有源代码,并提出任何问题。

文章来源:https://dev.to/eleazar0425/mvvm-pattern-sample-in-swiftios--58aj