6-12.【架构设计】SwiftUI适合复杂业务架构吗?如何设计一个可测试、可扩展、可替换UI框架的SwiftUI架构?

3 阅读3分钟

一句话结论(先给判断)

SwiftUI 非常适合复杂业务架构,但前提是:你必须把 SwiftUI 限制在“渲染层”,让业务、状态、副作用完全独立于它。

SwiftUI 不应该成为你的架构核心
它应该是一个 可替换的 UI Adapter


一、什么时候 SwiftUI「不适合」复杂业务?

先把坑说清楚:

SwiftUI 不适合复杂业务,通常不是 SwiftUI 的问题,而是使用方式的问题

  • 把业务逻辑写进 View / ViewModel
  • @EnvironmentObject 当全局状态仓库
  • 状态粒度混乱,View 与业务强耦合
  • 副作用分散在 .task / onAppear

👉 结果是:
UI ≈ 业务,换 UI 就等于重写系统


二、复杂业务下的正确分层(核心)

你要的不是“SwiftUI 架构”,而是:

业务架构 + SwiftUI 适配层

推荐的五层结构(实践级)

┌──────────────────────────┐
│        SwiftUI View      │  ← 纯渲染
├──────────────────────────┤
│      UI Adapter Layer    │  ← ViewModel / Presenter
├──────────────────────────┤
│     State / Reducer      │  ← 业务状态机(纯)
├──────────────────────────┤
│   UseCase / Domain       │  ← 业务规则
├──────────────────────────┤
│ Infrastructure / IO      │  ← API / DB / SDK
└──────────────────────────┘

SwiftUI 只占最上面一层


三、一个“可替换 UI”的核心原则

UI 不拥有 State,只观察 State

正确关系:

State → UI
Action ← UI

而不是:

UI → State → UI

四、可测试的关键:纯业务核心

1️⃣ State + Reducer 是纯函数

struct OrderState {
    var items: [Item]
    var total: Double
}

enum OrderAction {
    case add(Item)
    case remove(Item)
}

func orderReducer(
    state: inout OrderState,
    action: OrderAction
) {
    switch action {
    case .add(let item):
        state.items.append(item)
        state.total += item.price
    case .remove(let item):
        ...
    }
}
  • 不依赖 SwiftUI
  • 不依赖线程
  • 不依赖时间

👉 100% 可单元测试


2️⃣ 副作用通过协议注入

protocol OrderService {
    func submit(_ order: OrderState) async throws
}
  • SwiftUI / UIKit 都只是调用方
  • 测试中可 mock

五、SwiftUI 层的正确角色

SwiftUI View 只做三件事

  1. 声明 UI
  2. 绑定 State 的投影
  3. 发 Action
struct OrderView: View {
    let state: OrderState
    let send: (OrderAction) -> Void

    var body: some View {
        List(state.items) { item in
            Text(item.name)
        }
        Button("Submit") {
            send(.submit)
        }
    }
}
  • View 无逻辑
  • 无副作用
  • 无业务判断

六、UI Adapter 层(被严重低估的一层)

你可以叫它:

  • ViewModel
  • Presenter
  • Store
  • Feature Adapter

职责:

  • 把业务 State 映射成 UI 需要的形态
  • 管理 UI 生命周期相关逻辑(但不进业务)
final class OrderViewAdapter: ObservableObject {
    @Published var viewState: OrderViewState

    func send(_ action: OrderAction) {
        ...
    }
}

👉 这一层是 SwiftUI 唯一“认识”的业务层


七、为什么这种架构“可替换 UI”?

因为:

  • UIKit / SwiftUI / Web
    都只需要实现:
render(State)
send(Action)

你的业务核心完全不变。


八、可扩展性的来源

1️⃣ Feature-level 隔离

  • 每个 Feature:

    • State
    • Action
    • Reducer
    • Adapter
  • 无跨 Feature 状态修改

2️⃣ 组合而不是继承

  • State 组合
  • Reducer 组合
  • View 组合

这正是 TCA / Redux 的优势。


九、什么时候用 TCA?什么时候不用?

TCA 非常适合:

  • 多 Feature 协作
  • 强一致性需求
  • 高并发 / 高可测试性

不必 TCA 的情况:

  • 页面级简单逻辑
  • 快速原型

你可以:

核心 Feature 用 TCA,边缘 Feature 用轻量 MVVM


十、真实项目里的“最低风险方案”

“TCA-like,不一定用 TCA”

  • 单一 State
  • Action 驱动
  • Reducer 纯函数
  • SwiftUI 只 render

你已经拿到 80% 的收益。


最终一句话(架构级)

SwiftUI 非常适合复杂业务,但前提是:
它只负责‘画’,而你的系统已经决定了‘发生什么’。