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 项目模板与代码片段

MVVM 项目模板与代码片段

标准 MVVM 项目结构模板

MyMVVMProject/
├── Models/               # 数据模型层
│   ├── Todo.swift        # 核心数据模型
│   └── NetworkModels/    # 网络相关模型(可选)
├── ViewModels/           # 视图模型层
│   ├── TodoListViewModel.swift
│   └── ViewModelFactory.swift # 集中管理 ViewModel 创建(可选)
├── Views/                # 视图层
│   ├── Components/       # 可复用组件
│   │   └── TodoCell.swift
│   ├── Screens/
│   │   └── TodoListView.swift
│   └── StyleGuide.swift  # 样式规范(可选)
├── Services/             # 服务层(网络/数据库)
│   ├── APIService.swift
│   └── StorageService.swift
├── Utilities/            # 工具类
│   ├── Extensions/
│   └── Logger.swift
└── Resources/            # 资源文件

核心代码片段

1. 基础 ViewModel 模板

import Combine

class BaseViewModel: ObservableObject {
    var cancellables = Set<AnyCancellable>()
    
    deinit {
        cancellables.forEach { $0.cancel() }
    }
}

class TodoListViewModel: BaseViewModel {
    @Published var todos: [Todo] = []
    @Published var isLoading = false
    @Published var error: Error?
    
    private let service: TodoServiceProtocol
    
    init(service: TodoServiceProtocol = TodoService()) {
        self.service = service
        super.init()
        loadTodos()
    }
    
    func loadTodos() {
        isLoading = true
        service.fetchTodos()
            .receive(on: DispatchQueue.main)
            .sink { [weak self] completion in
                self?.isLoading = false
                if case .failure(let error) = completion {
                    self?.error = error
                }
            } receiveValue: { [weak self] todos in
                self?.todos = todos
            }
            .store(in: &cancellables)
    }
}

2. 数据绑定示例

struct TodoListView: View {
    @StateObject var viewModel = TodoListViewModel()
    
    var body: some View {
        Group {
            if viewModel.isLoading {
                ProgressView()
            } else {
                List(viewModel.todos) { todo in
                    TodoCell(todo: todo)
                }
            }
        }
        .alert("Error", 
               isPresented: .constant(viewModel.error != nil),
               presenting: viewModel.error) { _ in
            Button("OK") { viewModel.error = nil }
        } message: { error in
            Text(error.localizedDescription)
        }
    }
}

3. 依赖注入模板

// 协议定义
protocol TodoServiceProtocol {
    func fetchTodos() -> AnyPublisher<[Todo], Error>
}

// 生产环境实现
class TodoService: TodoServiceProtocol {
    func fetchTodos() -> AnyPublisher<[Todo], Error> {
        // 实际网络请求实现
    }
}

// 测试环境 Mock
class MockTodoService: TodoServiceProtocol {
    var stubTodos: [Todo] = []
    var shouldFail = false
    
    func fetchTodos() -> AnyPublisher<[Todo], Error> {
        if shouldFail {
            return Fail(error: NSError(domain: "test", code: 500))
                .eraseToAnyPublisher()
        }
        return Just(stubTodos)
            .setFailureType(to: Error.self)
            .eraseToAnyPublisher()
    }
}

实用扩展片段

1. 简化绑定的 ViewModel 扩展

extension ViewModel {
    func bind<T>(
        _ publisher: AnyPublisher<T, Never>,
        to binding: Binding<T>
    ) -> AnyCancellable {
        publisher
            .receive(on: RunLoop.main)
            .assign(to: \.wrappedValue, on: binding)
    }
}

2. 调试 ObservableObject 的扩展

extension ObservableObject where Self.ObjectWillChangePublisher == ObservableObjectPublisher {
    func debugPrint(_ message: String) -> Self {
        objectWillChange
            .sink { _ in
                print("[\(Self.self)] \(message)")
            }
            .store(in: &cancellables)
        return self
    }
}

项目模板资源

  1. Xcode 项目模板
    推荐使用 SwiftUI-MVVM-Template 快速创建项目

  2. Sourcery 代码生成
    通过模板自动生成 MVVM 基础代码:

    # sourcery.yml
    templates:
      - MVVM/ViewModel.stencil
      - MVVM/View.stencil
    output:
      ViewModels/{{ name }}ViewModel.swift
      Views/{{ name }}View.swift
    
  3. Swift Package 模块化模板
    将各层拆分为独立模块:

    // Package.swift
    targets: [
        .target(name: "Models"),
        .target(name: "ViewModels", dependencies: ["Models"]),
        .target(name: "Views", dependencies: ["ViewModels"])
    ]
    

提示:所有代码片段都遵循 SwiftUI 3.0+ 和 Combine 最佳实践,建议在 Xcode 14+ 环境中使用

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