@Observable数据模型结合ReferenceFileDocument的环境注入及序列化问题
基于ReferenceFileDocument和@Observable的SwiftUI文档架构问题
我开发的一款App采用@Observable作为数据模型,视图通过@Environment(MyModel.self) private var myModel获取该模型。由于视图数量较多,@Observable相比@Published和@ObservedObject能更精准地重绘需要更新的视图。
最初App未实现文档持久化时,通过以下方式将数据模型注入环境:
@main struct MyApp: App { @State private var myModel = MyModel() var body: some Scene { WindowGroup { myDocumentView() .environment(myModel) } } }
我尝试将App改为基于文档的架构,因SwiftData在Codable支持上存在不便且不支持Undo管理器,而App仅需简单JSON序列化,故选择ReferenceFileDocument。我让数据模型MyModel遵循ReferenceFileDocument协议,代码如下:
import SwiftUI import SwiftData import UniformTypeIdentifiers @Observable class MyModel: Identifiable, Codable, @unchecked Sendable, ReferenceFileDocument { // Mark: ReferenceFileDocument protocol static var readableContentTypes: [UTType] { [.myuttype] } required init(configuration: ReadConfiguration) throws { if let data = configuration.file.regularFileContents { let decodedModel = try MyModel(json: data) if decodedModel != nil { self = decodedModel! } else { print("Unable to decode the document.") } } else { throw CocoaError(.fileReadCorruptFile) } } func snapshot(contentType: UTType) throws -> Data { try self.json() } func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper { FileWrapper(regularFileWithContents: snapshot) } var nodes = [Node]() // this is the actual data model init() { newDocument() } ... etc.
同时我也尝试过让ReferenceFileDocument作为独立模块序列化数据模型实例的方案,但目前仍面临两个问题:
- a) 无法将新建或反序列化后的
MyModel实例注入环境,使视图能利用其@Observable属性; - b) 虽能触发序列化,但序列化的是空数据模型实例。
我通过Undo管理器处理数据模型变更,代码如下:
// MARK: - Undo func undoablyPerform(_ actionName: String, with undoManager: UndoManager? = nil, doit: () -> Void) { let oldNodes = self.nodes doit() undoManager?.registerUndo(withTarget: self) { myself in self.undoablyPerform(actionName, with: undoManager) { self.nodes = oldNodes } } undoManager?.setActionName(actionName) }
顶层文档视图代码如下:
import SwiftUI import CoreGraphics struct myDocumentView: View { @Environment(MyModel.self) private var myModel @Environment(\.undoManager) var undoManager init(document: MyModel) { self.myModel = document // but of course this is wrong! } ... etc.
感谢用户malhal提供的使ReferenceFileDocument遵循@Observable的方案。
内容的提问来源于stack exchange,提问作者Andromeda
相关产品推荐
相关产品推荐

