Well, this trick with the UndoManager
worked for me on some Views; on others ist didn't. I was getting tired to spend hours in investigating why this was so. So I was looking for a way to save the document’s content explicitly. This is what I came up with.
In my app the document’s content is encoded as a PropertyList
. So why not writing this PropertyList
by myself? In MyDocument
I create my model and keep a reference on it. In my model I store the fileURL
of my document’s file on creating or opening a document:
@main
struct MyApp: App {
var body: some Scene {
DocumentGroup(newDocument: MyDocument()) { file in
ContentView(document: file.$document)
.environmentObject(file.document.model)
.task {
file.document.model.fileURL = file.fileURL
}
}
}
}
So I can access this URL in my model:
func save() {
if let url = fileURL { // we know our url
if url.startAccessingSecurityScopedResource() { // access file outside sandbox
if let outStream = OutputStream(url: url, append: false), // create the output stream
let data = try? PropertyListEncoder().encode(self) { // and encode ourself as a property list
outStream.open() // open the output stream
let _ = outStream.write(data) // and write the property list
outStream.close() // close the output stream
}
url.stopAccessingSecurityScopedResource() // stop secure access
}
}
}
The only problem with this is that outStream.write
is not that simple as it looks here. It needs some kind of buffer pointer which is not that easy to create in Swift 5. But here is this nice extension which does the job for me:
extension OutputStream {
// from: https://stackoverflow.com/questions/55829812/writing-data-to-an-outputstream-with-swift-5
func write(_ data: Data) -> Int {
return data.withUnsafeBytes({ (rawBufferPointer: UnsafeRawBufferPointer) -> Int in
let bufferPointer = rawBufferPointer.bindMemory(to: UInt8.self)
return self.write(bufferPointer.baseAddress!, maxLength: data.count)
})
}
}
Now I can save the content of my document wherever I want it without using any dummy undo manager.