建筑师之家IOS / NOTES
← 全部笔记

架构

先划边界,再选 MVVM

MVC 或 MVVM 只是界面那一层怎么分工。工程变乱,通常是界面、状态、副作用缠在一起。

争论 MVC 还是 MVVM,经常停在“逻辑要不要从 ViewController 挪走”。挪走之后如果 ViewModel 里既请求网络、又 push 下一个页面、又直接改 UIKit,那只是换了个更难看的控制器。

三块

依赖方向只允许向内。界面向状态要数据,状态不 import UIKit。副作用实现细节不出现在界面文件里。这样换界面技术(多一个 SwiftUI 页面)时,状态和请求不用重写。

ViewModel 什么时候是有用的

它有用,是当一个界面的状态转移不适合写在 view 生命周期里:多次点击的取消、输入校验、分页拼接。它没用,是当它只是把每个 IBAction 原样包一层函数。

enum FeedEvent {
    case appear
    case loaded([Item])
    case failed
    case retry
}

struct FeedState {
    var items: [Item] = []
    var phase: Phase = .idle

    enum Phase { case idle, loading, failed, ready }

    mutating func send(_ event: FeedEvent) {
        switch (phase, event) {
        case (.idle, .appear), (.failed, .retry):
            phase = .loading
        case (.loading, .loaded(let items)):
            self.items = items
            phase = .ready
        case (.loading, .failed):
            phase = .failed
        default:
            break
        }
    }
}

这段没有 URLSession,也没有 navigationController。请求是否发出,由外面看到 phase 变成 loading 来决定。非法转移(已经在 loading 时再 appear)被默认分支丢掉。这种代码可以用单测覆盖,不需要把控制器跑起来。

命名随你。叫 Store、Model、Reducer 都可以。守住的是边界,不是文件夹名字。