How to implement openDocument(withContentsOf:display:completionHandler:)?

I'm new to macOS programming. In a macOS document based application I have create to learn, I'm having trouble implementing openDocument(withContentsOf:display:completionHandler:) to open a file document.


Would anyone be so kind to give me an example of use?


The code I've written so far is:


import Cocoa


class DocumentController: NSDocumentController {


    func openDoc() {
        let directoryURL = FileManager.default.urls(for: ..applicationSupportDirectory, in: .userDomainMask).first
      
        let docURL = URL(string:"SampleFile.doctest", relativeTo:directoryURL)
      
        openDocument(withContentsOf: docURL!, display: true, completionHandler: {(NSDocument, Bool, Error) in
          
            // incomplete
        }
}

Apologies in advance if I'm misunderstanding, but if you're new to macOS, subclassing NSDocumentController is absolutely the wrong place to start. It's exceeding rare that it's ever necessary to subclass NSDocumentController.


If you're writing a document-based application, start from the Xcode application template, and check the box in the setup options for a NSDocument-based app. This will provide an initial implementation of a NSDocument subclass (not NSDocumentController), with dummy implementations of the methods that are usually overridden.


You should also study this guide:


developer.apple.com/library/content/documentation/DataManagement/Conceptual/DocBasedAppProgrammingGuideForOSX/Introduction/Introduction.html


Although it's a bit old, it's still mostly accurate, and it's important to understand what's done for you, and what you're supposed to do. The NSDocument class has massive quantities of pre-defined behavior that you should adopt rather than re-invent.


In short, the basic approach is for you to provide, in your subclass, an override of one data-decoding method (for reads) and one data-encoding method (for saves). You will also provide information about the document type (in the Info page of the target for your app), and connect your UI to the data model that the document reads and saves. In the simplest cases, that's about all you need to do.

Hi QuinceyMorris, thank you for your answer very complete and useful. I already create the document base app where all the UI controls (Save As, Save, Open etc) for document handling, work fine. What I forget to mention in my question is that I would like to learn to programmatically save a document in Application Folder and the programmatically Open it.


To save it I used this code in View Controller:


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

        let docURL = URL(string:"SampleFile.doctest", relativeTo:directoryURL)

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


And this seems to work fine.


Now, I'm searching for a way to open it.

Accepted Answer

That code is for reading a document's contents, not for saving, so maybe you pasted the wrong source code?


Anyway, a couple of points:


— You should not put files directly in the Application Support folder. Instead, create a subfolder of Application Support whose name is the bundle ID of your app.


— The Application Support folder (or your app's subfolder of it) is not regarded as a user-visible location. That is, you should not open a file in there as a regular document. If that location contains something like template documents, then you should open a copy of the template. However, my exact advice here depends on what you're really trying to do.


— To "open" documents programmatically, the easiest way is to use NSDocumentController (but not override it, as you tried originally). NSDocumentController provides methods to create and open documents, etc, but you need to be sure to use the correct one.


Start by getting the shared instance of NSDocumentController: "NSDocumentController.shared ()". Then, to duplicate an existing document, use the "duplicateDocument(withContentsOf:copying:displayName:)".


— The best way to approach what you're doing (whatever you're doing) depends on whether your app is sandboxed, and if it is going to be distributed through the Mac App Store. The NSDocument class is very old, and has been changed in big ways several times. That makes it a bit hard to find the correct APIs to use in any situation.

Oops, the correct code was this one:



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

        let docURL = URL(string:"SampleFile.doctest", relativeTo:directoryURL)
        if (try? document.write(to: docURL!, ofType: "com.myCompany.doctest")) != nil {
        } else {
            print("An error occured")
        }
    }



Thank you for the useful advices, I will follow them.


What I'm trying to learn is how to create a sandboxed app that when launched opens automatically a window where the user can paste or type text to edit. User can also open new windows.


When user quits, all open windows containing text are stored as documents in a folder of Application Support. The winodws close individually by the user via close command are not stored.


In new session the app restores the saved windows.


The app should not have Open, Save UI elements, like in Safari.


I choose a document based app because I would like that the uses can open more than a window once and use tab browsing. Maybe it's not the correct choice.

I think it's OK to use NSDocument for this.


The only thing to be careful of is the document name in the title bar of the document window. There's a menu there, and the ability to rename the document. I'm not sure how to suppress those things, but you can try looking at:


developer.apple.com/reference/appkit/nsdocument/1515077-displayname


or:


developer.apple.com/reference/appkit/nswindowcontroller/1528112-windowtitle


One of those might disable that special title bar menu behavior.

Thank you so much for your help QuinceyMorris!

How to implement openDocument(withContentsOf:display:completionHandler:)?
 
 
Q