2026 年如何构建 SwiftUI 项目
发布: (2026年2月2日 GMT+8 09:00)
4 min read
原文: Dev.to
Source: Dev.to
问题
Xcode 只给你 ContentView.swift,仅此而已。随着你的应用增长,你会出现:
- 一个文件夹里有 50 个文件
- ViewModel 与 View 混在一起
- 没有明确的关注点分离
- 每次需要查找东西时都很痛苦
解决方案:基于特性的架构
MyApp/
├── App/
│ ├── MyAppApp.swift
│ ├── AppDelegate.swift (if needed)
│ └── AppConfiguration.swift
├── Features/
│ ├── Auth/
│ │ ├── Views/
│ │ │ ├── LoginView.swift
│ │ │ └── RegisterView.swift
│ │ ├── ViewModels/
│ │ │ └── AuthViewModel.swift
│ │ └── Models/
│ │ └── User.swift
│ ├── Home/
│ │ ├── Views/
│ │ ├── ViewModels/
│ │ └── Models/
│ └── Profile/
│ ├── Views/
│ ├── ViewModels/
│ └── Models/
├── Core/
│ ├── Network/
│ │ ├── NetworkManager.swift
│ │ ├── APIEndpoint.swift
│ │ └── APIError.swift
│ ├── Storage/
│ │ ├── StorageManager.swift
│ │ └── KeychainManager.swift
│ └── Extensions/
│ ├── View+Extensions.swift
│ └── String+Extensions.swift
├── UI/
│ ├── Components/
│ │ ├── PrimaryButton.swift
│ │ ├── LoadingView.swift
│ │ └── ErrorView.swift
│ └── Theme/
│ ├── Colors.swift
│ ├── Fonts.swift
│ └── Spacing.swift
└── Resources/
├── Assets.xcassets
└── Localizable.strings
为什么这样有效
1. 功能是自包含的
每个功能都拥有其所需的一切:
- 视图(UI)
- 视图模型(逻辑)
- 模型(数据)
添加新功能?创建一个新文件夹。删除功能?删除相应的文件夹。
2. 核心是共享逻辑
跨功能使用的内容:
- 网络
- 存储
- 扩展
只需在一个地方维护,所有地方都可使用。
3. UI 可复用
每个功能使用的组件和主题:
// UI/Theme/Colors.swift
import SwiftUI
enum AppColors {
static let primary = Color("Primary")
static let secondary = Color("Secondary")
static let background = Color("Background")
static let text = Color("Text")
}
// UI/Components/PrimaryButton.swift
import SwiftUI
struct PrimaryButton: View {
let title: String
let action: () -> Void
var body: some View {
Button(action: action) {
Text(title)
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(AppColors.primary)
.cornerRadius(12)
}
}
}
ViewModel 模式
每个功能的 ViewModel 都遵循相同的模式:
@MainActor
final class HomeViewModel: ObservableObject {
// MARK: - Published State
@Published private(set) var items: [Item] = []
@Published private(set) var isLoading = false
@Published private(set) var error: Error?
// MARK: - Dependencies
private let networkManager: NetworkManager
init(networkManager: NetworkManager = .shared) {
self.networkManager = networkManager
}
// MARK: - Public Methods
func loadItems() async {
isLoading = true
error = nil
do {
items = try await networkManager.fetch(ItemsEndpoint())
} catch {
self.error = error
}
isLoading = false
}
}
关键点
@MainActor确保 UI 更新在主线程上进行。private(set)防止外部对属性进行修改。- 依赖注入便于测试。
导航
简易应用 – 原生 SwiftUI 导航
NavigationStack {
HomeView()
.navigationDestination(for: Item.self) { item in
DetailView(item: item)
}
}
复杂应用 – 协调器模式
@MainActor
final class AppCoordinator: ObservableObject {
@Published var path = NavigationPath()
enum Destination: Hashable {
case detail(Item)
case settings
case profile
}
func navigate(to destination: Destination) {
path.append(destination)
}
func pop() {
path.removeLast()
}
func popToRoot() {
path.removeLast(path.count)
}
}
快速设置脚本
自动创建结构:
#!/bin/bash
mkdir -p App
mkdir -p Features/{Auth,Home,Profile}/{Views,ViewModels,Models}
mkdir -p Core/{Network,Storage,Extensions}
mkdir -p UI/{Components,Theme}
mkdir -p Resources
我的完整模板
我已将所有内容(以及实际代码)打包成一个入门模板,包含:
- 5 个准备好的屏幕
- 20+ 个 UI 组件
- 使用 async/await 的
NetworkManager - 暗模式支持
- MVVM 架构
如果你想跳过设置,请访问 查看。
TL;DR
- Feature‑based structure – 按功能分组,而不是按文件类型。
- Core – 共享的工具。
- UI – 可复用的组件和主题。
- MVVM – 使用
@MainActor的 ViewModel。 - Be consistent – 在所有地方都使用相同的模式。