SwiftUI MVVM架构下笔记选择与更新的规范实现(含服务器交互场景)
SwiftUI MVVM架构下笔记选择与更新的规范实现(含服务器交互场景)
你遇到的列表+详情页更新同步的问题,确实是SwiftUI MVVM模式下非常典型的场景——既要处理服务器端的PUT请求,还要在更新成功后同步列表和详情页的数据,同时避免返回列表时数据被重置的尴尬。下面我结合你的代码,一步步给出规范的改造方案:
一、明确Model的核心职责
首先要让NoteModel成为唯一的数据源,封装所有数据逻辑(列表加载、单条更新、网络请求),视图只负责展示和触发动作,完全不关心数据的来源和处理细节。这里我们用SwiftUI 5+的@Observable特性,让Model的变化自动驱动视图刷新:
import Foundation @Observable class NoteModel { var notes: [Note] = [] var selectedNote: Note? private var isNotesFetched = false // 标记列表是否已加载,避免重复触发请求 // 加载笔记列表(模拟网络请求) func fetchNotes() async { guard !isNotesFetched else { return } // 模拟网络延迟 try? await Task.sleep(nanoseconds: 500_000_000) // 真实场景替换为GET请求获取列表 self.notes = [ Note(title: "First note", content: "Test note"), Note(title: "Reminder", content: "Don't forget to water the plants!"), Note(title: "Shopping list", content: "Eggs, milk, hat, bread") ] isNotesFetched = true } // 更新笔记(封装PUT请求+同步最新数据) func updateNote(_ targetNote: Note, newTitle: String, newContent: String) async throws { // 1. 模拟向服务器发送PUT请求 try? await Task.sleep(nanoseconds: 300_000_000) // 真实场景替换为: // let updatedNote = try await APIClient.updateNote(targetNote.id, title: newTitle, content: newContent) // 2. 模拟服务器返回处理后的最新笔记(比如服务器自动添加标记) let updatedNote = Note(id: targetNote.id, title: "\(newTitle) [Server Updated]", content: newContent) // 3. 同步更新列表和选中的笔记 if let index = notes.firstIndex(where: { $0.id == targetNote.id }) { notes[index] = updatedNote } if selectedNote?.id == targetNote.id { selectedNote = updatedNote } } // 可选:单独拉取单条最新笔记(比如更新后需要强制刷新) func fetchLatestNote(forId noteId: UUID) async { try? await Task.sleep(nanoseconds: 300_000_000) guard let index = notes.firstIndex(where: { $0.id == noteId }) else { return } let latestNote = Note(id: noteId, title: notes[index].title, content: notes[index].content + " (Synced from server)") notes[index] = latestNote selectedNote = latestNote } } // 数据模型(保持Identifiable、Hashable方便列表和导航使用) struct Note: Identifiable, Hashable { let id = UUID() var title: String var content: String // 可选:添加初始化方法方便测试 init(title: String, content: String) { self.title = title self.content = content } }
二、列表页:简化视图逻辑,只做展示和导航
列表页不需要直接处理数据绑定,只需要展示model.notes,并在点击时标记selectedNote,同时解决之前返回列表数据重置的问题(通过isNotesFetched只加载一次,下拉刷新可强制重新加载):
import SwiftUI struct NoteListView: View { @State private var model = NoteModel() var body: some View { NavigationStack { List(model.notes) { note in NavigationLink { NoteDetailView(model: model, note: note) } label: { Text(note.title) } .onTapGesture { model.selectedNote = note // 点击时标记选中的笔记 } } .navigationTitle("My Notes") .task { await model.fetchNotes() // 视图出现时加载列表(仅一次) } .refreshable { await model.fetchNotes() // 下拉刷新时强制重新加载列表 } } } }
三、详情页:专注用户输入,调用Model完成更新
详情页负责收集用户的编辑内容,然后调用Model的updateNote方法,所有数据更新逻辑交给Model处理,视图只需要处理加载状态和错误提示:
struct NoteDetailView: View { @ObservedObject var model: NoteModel let note: Note @State private var editedTitle: String @State private var editedContent: String @State private var isUpdating = false // 标记是否正在更新 // 初始化时填充当前笔记内容 init(model: NoteModel, note: Note) { self.model = model self.note = note self._editedTitle = State(initialValue: note.title) self._editedContent = State(initialValue: note.content) } var body: some View { Form { TextField("Note Title", text: $editedTitle) .disabled(isUpdating) TextEditor(text: $editedContent) .frame(height: 200) .disabled(isUpdating) Button("Save Changes") { Task { isUpdating = true do { try await model.updateNote(note, newTitle: editedTitle, newContent: editedContent) // 可选:更新成功后提示用户 print("Note updated successfully!") } catch { // 处理更新错误,比如弹出Alert print("Failed to update note: \(error.localizedDescription)") } isUpdating = false } } .disabled(isUpdating || editedTitle.isEmpty) } .navigationTitle("Edit Note") .navigationBarTitleDisplayMode(.inline) } }
四、核心设计思路说明
- 职责分离:Model管数据(网络、存储、更新),视图管展示和交互,完全符合MVVM的设计原则,后续修改网络逻辑只需要改Model,不需要动视图。
- 自动刷新:因为
NoteModel是@Observable,当notes或selectedNote变化时,所有依赖这些数据的视图(列表、详情)会自动刷新,不需要手动发布通知。 - 避免数据重置:通过
isNotesFetched标记,fetchNotes只会在首次加载时调用,返回列表时不会重复请求导致数据被覆盖;下拉刷新则可以强制重新加载。 - 服务器同步优先:更新操作不会直接修改本地数据,必须等服务器返回最新版本后再更新Model,确保本地数据和服务器完全一致,避免本地修改和服务器冲突。
内容来源于stack exchange
相关产品推荐
相关产品推荐

