SwiftUI vs UIKit
Apple 现代声明式框架 SwiftUI 与久经考验的 UIKit 之间的全面对比。2025 年你该选择哪个框架?
Model-View-ViewModel:直观、灵活、使用广泛
The Composable Architecture:函数式、可测试、可预测
| 分类 | MVVM | TCA |
|---|---|---|
| 性能 | 8/10 | 8/10 |
| 学习难易度 | 9/10 | 4/10 |
| 生态系统 | 9/10 | 7/10 |
| 社区 | 9/10 | 7/10 |
| 就业市场 | 10/10 | 7/10 |
| 面向未来 | 8/10 | 9/10 |
// MVVM - 商品列表
import SwiftUI
import Observation
@Observable
class ProductListViewModel {
var products: [Product] = []
var isLoading = false
var errorMessage: String?
var searchText = ""
private let repository: ProductRepository
init(repository: ProductRepository = .live) {
self.repository = repository
}
var filteredProducts: [Product] {
guard !searchText.isEmpty else { return products }
return products.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
func loadProducts() async {
isLoading = true
errorMessage = nil
do {
products = try await repository.fetchProducts()
} catch {
errorMessage = "商品加载失败:\(error.localizedDescription)"
}
isLoading = false
}
func deleteProduct(_ product: Product) async {
do {
try await repository.delete(product.id)
products.removeAll { $0.id == product.id }
} catch {
errorMessage = "删除失败:\(error.localizedDescription)"
}
}
}
struct ProductListView: View {
@State private var viewModel = ProductListViewModel()
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading {
ProgressView("加载中...")
} else {
List(viewModel.filteredProducts) { product in
ProductRow(product: product)
}
.searchable(text: $viewModel.searchText)
}
}
.navigationTitle("商品")
}
.task { await viewModel.loadProducts() }
.alert("错误", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("确定") { viewModel.errorMessage = nil }
} message: {
Text(viewModel.errorMessage ?? "")
}
}
}// TCA - 商品列表
import ComposableArchitecture
import SwiftUI
@Reducer
struct ProductListFeature {
@ObservableState
struct State: Equatable {
var products: [Product] = []
var isLoading = false
var errorMessage: String?
var searchText = ""
var filteredProducts: [Product] {
guard !searchText.isEmpty else { return products }
return products.filter { $0.name.localizedCaseInsensitiveContains(searchText) }
}
}
enum Action {
case onAppear
case searchTextChanged(String)
case deleteProduct(id: String)
case productsLoaded(Result<[Product], Error>)
case productDeleted(Result<Void, Error>)
case dismissError
}
@Dependency(\.productRepository) var repository
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onAppear:
state.isLoading = true
return .run { send in
await send(.productsLoaded(
Result { try await repository.fetchProducts() }
))
}
case .searchTextChanged(let text):
state.searchText = text
return .none
case .deleteProduct(let id):
return .run { send in
await send(.productDeleted(
Result { try await repository.delete(id) }
))
}
case .productsLoaded(.success(let products)):
state.isLoading = false
state.products = products
return .none
case .productsLoaded(.failure(let error)):
state.isLoading = false
state.errorMessage = error.localizedDescription
return .none
case .productDeleted(.success):
return .send(.onAppear)
case .productDeleted(.failure(let error)):
state.errorMessage = error.localizedDescription
return .none
case .dismissError:
state.errorMessage = nil
return .none
}
}
}
}
struct ProductListView: View {
let store: StoreOf<ProductListFeature>
var body: some View {
WithPerceptionTracking {
NavigationStack {
List(store.filteredProducts) { product in
Text(product.name)
}
.searchable(text: store.binding(get: \.searchText, send: ProductListFeature.Action.searchTextChanged))
.navigationTitle("商品")
}
.task { store.send(.onAppear) }
}
}
}中小型项目适合 MVVM——开发速度更快,入门门槛更低。大型、复杂且对测试要求严格的项目适合 TCA——可预测的状态管理和可组合架构的价值会在长期项目中逐渐显现。混合方案同样可行:核心功能用 MVVM,关键/复杂流程用 TCA。
获取免费咨询掌握基础概念大约需要 1-2 周,达到高效产出水平大约需要 1-2 个月。建议观看 Point-Free 的视频并研究 TCA 的示例仓库。