You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:19:53