3

I'm new to macOS programming and I created a NSDocument app project to learn this architecture.

All works correctly, I can create a document, save it, and open one from Finder using standard UI controls.

I'm trying to save and open a document programmatically. I implemented these two actions. Saving works fine, but read don't work. Edit: I mean that the document window is not shown.

I would be very grateful if somebody could tell me what I'm doing wrong.

// this seems to work because the document is created and I can open it with drag and drop over the app
@IBAction func saveButtonPressed(_ sender: Any) {
    let directoryURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first // or .applicationSupportDirectory

    let docURL = URL(string:"MyFile.docuExperiments", relativeTo:directoryURL)

    if (try? document.write(to: docURL!, ofType: "com.myCompany.docuExperiments")) != nil {
    } else {
        print("An error occured")
    }
}

// the document window is not shown
@IBAction func loadButtonPressed(_ sender: Any) {
    let directoryURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first // or .applicationSupportDirectory

    let docURL = URL(string:"MyFile.docuExperiments", relativeTo:directoryURL)

    if (try? document.read(from: docURL!, ofType: "com.myCompany.docuExperiments")) != nil {
    } else {
        print("An error occured")
    }
}
Cue
  • 2,952
  • 3
  • 33
  • 54
  • Can you be more specific than “read don't work”? What happens, exactly? – rob mayoff May 25 '17 at 15:25
  • Should `loadButtonPressed` create a new document object or reload an existing document object? – Willeke May 25 '17 at 15:28
  • Hi rob, the document window don't show. I will update the question thank you. – Cue May 25 '17 at 16:40
  • Hi Willeke My intention was the one of open a closed document. – Cue May 25 '17 at 16:41
  • Use `NSDocumentController`s `openDocument(withContentsOf:display:completionHandler:)` to open. Use `NSDocument`s `saveToURL:ofType:forSaveOperation:completionHandler:` to save. – Willeke May 26 '17 at 00:43

2 Answers2

4

This seems to work.

Save the document:

@IBAction func saveButtonPressed(_ sender: Any) {
    let directoryURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first

    let docURL = URL(string:"MyFile.docuExperiments", relativeTo:directoryURL)

    if (try? document.write(to: docURL!, ofType: "com.myCompany.docuExperiments")) != nil {
    } else {
        print("An error occured")
    }
}

Open the document:

@IBAction func loadButtonPressed(_ sender: Any) {
    let documentController = NSDocumentController.shared()

    let directoryURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first

    let docURL = URL(string:"MyFile.docuExperiments", relativeTo:directoryURL)

    documentController.openDocument(withContentsOf: docURL!, display: true) {
        // completionHandler (NSDocument?, Bool, Error?)
        (document, documentWasAlreadyOpen, error) in
        if error != nil
        { print("An error occured")
        } else {
            if documentWasAlreadyOpen
            {
                print("documentWasAlreadyOpen: true")
            } else {
                print("documentWasAlreadyOpen: false")
            }
        }
    }

}

(thanks to Willeke for the tips).

Cue
  • 2,952
  • 3
  • 33
  • 54
1

NSDocument manages the display of a document from a URL. It's NSDocumentController that should be used to initiate the opening & saving of documents (although, as you have discovered, there's nothing stopping NSDocument from writing a copy of a document).

From the docs:

NSDocumentController Creates and Manages Documents

An app’s NSDocumentController object manages the documents in an app. In the MVC design pattern, an NSDocumentController object is a high-level controller. It has the following primary responsibilities:

  • Creates empty documents in response to the New item in the File menu
  • Creates documents initialized with data from a file in response to the Open item in the File menu
  • Tracks and manages those documents
  • Handles document-related menu items, such as Open Recent

(my emphasis)

IF you want to use your own routine, you need to subclass NSDocumentController and put your open routine in there. But it already has all of that logic, so really all you need to do is hook your button to the target of File -> Open, which will be func beginOpenPanel(completionHandler: @escaping ([URL]?) -> Void) in the default NSDocumentController. So there is no need to do so. Just read the NSDocumentController docs (linked above)

Grimxn
  • 22,115
  • 10
  • 72
  • 85
  • No, don't subclass `NSDocumentController` to open a document. – Willeke May 25 '17 at 15:23
  • Did you read my answer to the end? The OP's question is about file dialogs, and I point out that *there is no need to do so*. However if he wants to implement his routines as written that's the place to do it. But he doesn't need to. – Grimxn May 25 '17 at 15:25
  • 2
    Put the open routine (action method of the button) in the app delegate, not in a subclass of `NSDocumentController`. Use `NSDocumentController` as is. – Willeke May 25 '17 at 15:41