Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 第二部分:MVVM 在 SwiftUI 中的实现

第二部分:MVVM 在 SwiftUI 中的实现

第5章:开发 ViewModel 层

处理用户输入与业务逻辑

在 MVVM 架构中,ViewModel 层是连接 View 和 Model 的桥梁,负责处理用户输入并执行业务逻辑。本节将深入探讨如何在 SwiftUI 中实现这一关键功能。

1. 用户输入的响应机制

  • 输入绑定:通过 @Published 属性包装器和 ObservableObject 协议,ViewModel 可以暴露可观察的属性,供 View 层绑定。
  • 事件处理:ViewModel 提供方法供 View 调用,例如按钮点击或表单提交。这些方法通常修改 Model 数据或触发网络请求。
class TodoViewModel: ObservableObject {
    @Published var todos: [Todo] = []
    
    func addTodo(title: String) {
        let newTodo = Todo(title: title, isCompleted: false)
        todos.append(newTodo)
    }
    
    func toggleCompletion(for todo: Todo) {
        if let index = todos.firstIndex(where: { $0.id == todo.id }) {
            todos[index].isCompleted.toggle()
        }
    }
}

2. 业务逻辑的实现原则

  • 单一职责:每个 ViewModel 方法应只负责一个明确的业务逻辑。
  • 可测试性:避免在 ViewModel 中直接引用 SwiftUI 视图或环境对象,以便于单元测试。
  • 副作用管理:异步操作(如网络请求)应通过 Combine 或 Swift Concurrency 处理,并确保线程安全。

3. 与 Model 层的交互

  • 数据转换:ViewModel 负责将 Model 数据转换为 View 可直接使用的格式(例如日期格式化)。
  • 持久化操作:调用 Model 层的方法保存或加载数据,通常通过依赖注入解耦。
func loadTodos() async {
    do {
        let fetchedTodos = try await TodoAPI.fetchTodos()
        DispatchQueue.main.async { self.todos = fetchedTodos }
    } catch {
        handleError(error)
    }
}

4. 错误处理与用户反馈

  • 错误传递:通过 @Published 属性或 Combine 的 Error 事件通知 View 层。
  • 状态管理:使用枚举定义加载状态(如 loading/success/failure),驱动 UI 更新。
enum DataState {
    case idle, loading, loaded, error(Error)
}

class TodoViewModel: ObservableObject {
    @Published var state: DataState = .idle
    
    func loadTodos() {
        state = .loading
        Task {
            do {
                let todos = try await TodoAPI.fetchTodos()
                state = .loaded(todos)
            } catch {
                state = .error(error)
            }
        }
    }
}

案例:Todo 应用的业务逻辑实现

假设我们需要实现一个支持搜索的 Todo 列表:

  1. ViewModel 暴露 searchText 属性和 filteredTodos 计算属性。
  2. 使用 Combine 的 debounce 避免频繁搜索请求。
  3. 在 View 中通过 TextField 绑定 searchText,并显示 filteredTodos。
class TodoViewModel: ObservableObject {
    @Published var todos: [Todo] = []
    @Published var searchText: String = ""
    
    var filteredTodos: [Todo] {
        guard !searchText.isEmpty else { return todos }
        return todos.filter { $0.title.localizedCaseInsensitiveContains(searchText) }
    }
}

通过以上模式,ViewModel 能有效隔离业务逻辑与 UI,保持代码的可维护性和可测试性。

Last Updated:: 4/25/25, 8:11 PM