Since OS X v10.7, NSDocument
provides a far simpler mechanism you can override in subclasses: -presentedItemDidChange
.
Handling -presentedItemDidChange
, Ignoring Metadata Changes
Just relying on this callback can produce false positives, though, when metadata change. That got on my nerves quickly for files stored in Dropbox, for example.
My approach to deal with this in general, in Swift, is like this:
class MyDocument: NSDocument {
// ...
var canonicalModificationDate: Date!
override func presentedItemDidChange() {
guard fileContentsDidChange() else { return }
guard isDocumentEdited else {
DispatchQueue.main.async { self.reloadFromFile() }
return
}
DispatchQueue.main.async { self.showReloadDialog() }
}
fileprivate func showReloadDialog() {
// present alert "do you want to replace your stuff?"
}
/// - returns: `true` if the contents did change, not just the metadata.
fileprivate func fileContentsDidChange() -> Bool {
guard let fileModificationDate = fileModificationDateOnDisk()
else { return false }
return fileModificationDate > canonicalModificationDate
}
fileprivate func fileModificationDateOnDisk() -> Date? {
guard let fileURL = self.fileURL else { return nil }
let fileManager = FileManager.default
return fileManager.fileModificationDate(fileURL: fileURL)
}
}
Now you have to update the canonicalModificationDate
in your subclass, too:
- In a callback from the "do you want to replace contents?" alert which I call
-ignoreLatestFileChanges
so you don't nag your user ad infitium;
- In
-readFromURL:ofType:error:
or however you end up reading in contents for the initial value;
- In
-dataOfType:error:
or however you produce contents to write to disk.
FSEvents
work? – LeddapresentedItemDidChange
in NSFilePresenter (which NSDocument conforms to). Looking to see if there's something that shows the UI to ask the user what to do as well though. – Bise- (NSDate *)fileModificationDate
– Gaudet