I had noticed an unsettling behaviour about NSDocument
some years ago and created FB7392851, but the feedback didn't go forward, so I just updated it and hopefully here or there someone can explain what's going on.
When running a simple document-based app with a text view, what I type before closing the app may be discarded without notice. To reproduce it, you can use the code below, then:
- Type "asdf" in the text view.
- Wait until the Xcode console logs "saving". You can trigger it by switching to another app and back again.
- Type something else in the text view, such as "asdf" on a new line.
- Quit the app.
- Relaunch the app. The second line has been discarded.
Am I doing something wrong or is this a bug? Is there a workaround?
class ViewController: NSViewController {
@IBOutlet var textView: NSTextView!
}
class Document: NSDocument {
private(set) var text = ""
override class var autosavesInPlace: Bool {
return true
}
override func makeWindowControllers() {
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController
(windowController.contentViewController as? ViewController)?.textView.string = text
self.addWindowController(windowController)
}
override func data(ofType typeName: String) throws -> Data {
Swift.print("saving")
text = (windowControllers.first?.contentViewController as? ViewController)?.textView.string ?? ""
return Data(text.utf8)
}
override func read(from data: Data, ofType typeName: String) throws {
text = String(decoding: data, as: UTF8.self)
(windowControllers.first?.contentViewController as? ViewController)?.textView.string = text
}
}
What you described is an as-designed behavior. Basically, the autosave
feature automatically saves the document (if there are unsaved changes) every once a while, or when some events happen (such as when the app is activated or deactivated). If you made some changes on the document, and kill the app before autosave
is triggered, the changes will be lost.
On macOS, a user can kill an app in different ways – They can run the Quit
app menu by pressing CMD+Q
, run the Finder > Force Quit... menu, or execute kill -9
from Terminal.app
, or even turn off the power.
To avoid losing the unsaved changes, folks typically implement applicationShouldTerminate(_:)
, which is triggered when the user runs the Quit
app menu (CMD+Q
), and save the document from there.
Force Quit
and kill -9
are meant to quit the app immediately, and so there is no good way to run your code when the events happen. I won't worry too much though, because when a user quits an app in those ways, they are clear that they'd risk data loss.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.