My team and I have been working on an existing, non-document-based Cocoa application. This is our first Cocoa app, although we\'ve done a number of iOS apps thus far.
Th
Everything @Hans' answer provides is correct, but the final change that is needed is totally, stupidly trivial, but adds a nontrivial amount of functionality that comes with document-based apps:
In the Main.storyboard
file, the document
element has an extra property that needs to be deleted: initialViewController="XXX-XX-XXX"
.
It is probably the last thing on second line. Remove this, and the Save…
menu option, along with a few other menu options, will properly be enabled by default, and the app will properly recognize the document object upon launch.
This is more of an opinion than a direct answer, but if you're new to the Mac side and to document-based applications, your path of least resistance would definitely be to create a new doc-based Xcode project from the template and move your relevant code over, plugging it into the template places where needed.
An often found suggestion is to create a new document based application and move all you existing code in there. This can be cumbersome for a large workspace with all kinds of stuff nicely configured. Let alone breaking version control.
I took the following simple steps and it worked:
from this this generated project, copy the following section from the Info.plist (open the file with a normal text-editor):
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mydoc</string>
</array>
<key>CFBundleTypeIconFile</key>
<string></string>
<key>CFBundleTypeName</key>
<string>DocumentType</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).Document</string>
</dict>
</array>
and paste it in the Info.plist file in your own project.
Copy Document.swift from the generated document-based project into your own project.
it contains a method:
override func makeWindowControllers() {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: "Document Window Controller") as! NSWindowController
self.addWindowController(windowController)
}
It creates a new window just the way you application normally would. If you storyboard has only one windowcontroller the 'withIDentifier'-field can contain something arbitrary. If you have more window controllers in your storyboard, the identifier needs to correspond to the right windowcontroller for new documents.
Okay, this time I legitmately do have a solution to present.
It turns out I had a "window" instance variable in SPDocumentInfo (which as you'd guess pointed to the NSWindow associated with the document). That appeared to caused a chain of events (or more likely, prevented a chain of events) which led to SPDocumentInfo's dealloc not being called when it should have. I didn't catch that when I was comparing my project to the sample doc-based project, because apparently SPDocument also has a member variable called "window" which is also connected to the relevant NSWindow. I saw that connection in the sample project, and it looked identical to my project's connection, so I didn't think twice about it.
In other words, part of my problem was that I just coincidentally decided to connect up a "window" outlet NSDocument implementation, and didn't realize that I was actually shadowing a superclass variable (which I'm guessing is, unlike mine, configured as "assign" and not "retain").
So, things seem okay at this point, and I think I can declare that it is indeed possible (and my nagging issue notwithstanding, generally painless) to convert from a non-doc-based app to a doc-based one.