开发者

Converting a basic Cocoa application to a document-based application

开发者 https://www.devze.com 2023-02-13 05:30 出处:网络
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.

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.

The app really should be document-based, though, so I've begun trying to convert it. But things here & there don't seem to be working. For example, the File -> Open menu item is permanently disabled (although I finally got the File -> Save menu item to enabled; initially it wouldn't). In addition, I can click the red X to close a window, although the File -> Close menu item itself is disabled; however, when I close the window via the X button, the dealloc method in my NSDocument implementation (SPDocumentInfo) is not invoked. I created a sample, brand-new document-based app just for comparisons; when I close a window there, the SPDocument implementation's dealloc method is indeed invoked (as I'd expect.) So that concerns me.

I made a lot of changed to the project here and there; they include:

  • Made SPDocumentInfo extend SPDocument like so in the .h file:

    @interface SPDocumentInfo : NSDocument <NSWindowDelegate>
    
  • Implemented the following in SPDocumentInfo:

    - (NSString *)windowNibName {
        return @"SPDocument";
    }
    
    - (void)windowControllerDidLoadNib:(NSWindowController *) aController {
        [super 开发者_运维技巧windowControllerDidLoadNib:aController];
    }
    
    - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
        NSString *xml = [self toXml];
        return [xml dataUsingEncoding:NSUTF8StringEncoding];
    }
    
    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
        // will make this work later
        if ( outError != NULL ) {
            *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
        }
        return YES;
    }
    
  • Edited the .plist file to add "Document types". Among other things, defined "Cocoa NSDocument Class" = "SPDocumentInfo".

  • Altered some connections in SPDocumentInfo to match the connections in the sample document-based app. For example, in SPDocument.nib, the File's Owner (which represents SPDocumentInfo) is the Window's delegate.

So, I'm wondering if there are other sorts of things I might be missing in converting to a doc-based app. Or, are there any guides on how to do this? (I've looked but couldn't find any). Or should I just start over with a new document-based app and try to retrofit all of our stuff into it? In general, does anyone have any experience with this?


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:

  • Generate a document based application
  • 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.


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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消