你可以使用 ViewModel
和 Repository
模式来统一管理 SwiftData
数据。这种模式有助于分离数据逻辑与视图逻辑,使代码更加模块化和可维护。
1. 定义 Repository 类
Repository
类负责与 SwiftData
交互,处理数据的增删改查操作。它封装了数据访问逻辑,可以被多个 ViewModel
重用。
import SwiftData
class TodoRepository {
private let modelContext: ModelContext
init(modelContext: ModelContext) {
self.modelContext = modelContext
}
// Fetch all todos for a specific user
func fetchTodos(for user: User) -> [TodoItem] {
let fetchRequest = FetchRequest<TodoItem>(predicate: #Predicate { $0.user == user })
return (try? modelContext.fetch(fetchRequest)) ?? []
}
// Add a new todo
func addTodoItem(title: String, for user: User) {
let newItem = TodoItem(title: title, user: user)
modelContext.insert(newItem)
}
// Delete a todo item
func deleteTodoItem(_ item: TodoItem) {
modelContext.delete(item)
}
// Toggle completion of a todo item
func toggleCompletion(for item: TodoItem) {
item.isCompleted.toggle()
}
}
2. 定义 ViewModel
ViewModel
使用 Repository
来管理数据,并将数据传递给视图。它充当视图和数据层之间的桥梁。
import SwiftUI
class TodoViewModel: ObservableObject {
@Published var todos: [TodoItem] = []
@Published var selectedUser: User?
private let repository: TodoRepository
init(repository: TodoRepository) {
self.repository = repository
}
// Fetch todos for the selected user
func fetchTodos() {
if let user = selectedUser {
todos = repository.fetchTodos(for: user)
} else {
todos = []
}
}
// Add a new todo item
func addTodoItem(title: String) {
if let user = selectedUser {
repository.addTodoItem(title: title, for: user)
fetchTodos()
}
}
// Delete a todo item
func deleteTodoItem(at offsets: IndexSet) {
for index in offsets {
let item = todos[index]
repository.deleteTodoItem(item)
}
fetchTodos()
}
// Toggle completion status
func toggleCompletion(for item: TodoItem) {
repository.toggleCompletion(for: item)
fetchTodos()
}
// Set the selected user
func selectUser(_ user: User) {
selectedUser = user
fetchTodos()
}
}
3. 使用 ViewModel 和 Repository 在 SwiftUI 中
在视图中使用 ViewModel
来绑定数据,并响应用户的交互操作。
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@StateObject private var viewModel: TodoViewModel
init() {
// Initialize the repository and view model
let repository = TodoRepository(modelContext: modelContext)
_viewModel = StateObject(wrappedValue: TodoViewModel(repository: repository))
}
var body: some View {
NavigationView {
VStack {
// User selection
if let users = try? modelContext.fetch(User.all()) {
Picker("Select User", selection: $viewModel.selectedUser) {
ForEach(users, id: \.id) { user in
Text(user.name).tag(user as User?)
}
}
.pickerStyle(MenuPickerStyle())
.padding()
}
// Todo list
List {
ForEach(viewModel.todos) { item in
HStack {
Text(item.title)
Spacer()
Button(action: {
viewModel.toggleCompletion(for: item)
}) {
Image(systemName: item.isCompleted ? "checkmark.circle.fill" : "circle")
}
.buttonStyle(PlainButtonStyle())
}
}
.onDelete(perform: viewModel.deleteTodoItem)
}
// Add new todo item
HStack {
TextField("New todo", text: $newItemTitle)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button(action: {
viewModel.addTodoItem(title: newItemTitle)
newItemTitle = ""
}) {
Image(systemName: "plus")
.padding()
}
}
}
.navigationTitle("Todo List")
.onAppear {
viewModel.fetchTodos()
}
}
}
}
4. 解释代码
TodoRepository
: 封装了所有与SwiftData
的交互逻辑。它负责管理TodoItem
的 CRUD 操作。TodoViewModel
: 使用TodoRepository
来执行数据操作,并将结果发布给视图。它负责管理视图状态,例如todos
和selectedUser
。ContentView
: 使用TodoViewModel
绑定 UI 和数据。ViewModel
的状态变化会自动反映在视图中。
5. 优点
- 分离关注点:
ViewModel
负责处理视图逻辑,Repository
负责处理数据逻辑。这使得代码更易于维护和测试。 - 代码复用: 多个
ViewModel
可以共享相同的Repository
,避免重复数据访问逻辑。 - 模块化: 可以独立测试
Repository
和ViewModel
,提高代码的可靠性。
这样,你可以更好地管理和组织 SwiftData
数据,特别是在复杂的应用程序中。