|Home » Learning Curve
A look at Cocoa document-based application architecture.
The programming team at NeXT were aware that most applications are document-based. It's a simple procedure repeated over and over again: either start a new file or open an existing one, edit it and save it.
Performing such routine tasks on other systems such as Windows can be a lugubrious pain and bother. Developers have to allocate an OPENFILENAME structure, fill it in, pass it to a system navigation service, pluck out the name chosen by the user, and use that name to open a file or save it to disk.
When the user wants to close a window, the program - not the system - must check to see if the file has been edited since the last save, and if so must create a panel of its own to prompt the user to save it. Nothing is for free.
Before a new file is saved it of course has no name, and the application itself must keep track of this and suggest a suitable name when the user gets around to saving it.
Worse: as Windows and many Linux platforms do not admit of true object orientation on the desktop but have only one window per application, any attempt to open a new file in a running application must be met from the developer by a check whether the current file has been edited, and if so loop back to the 'close window' code above, issuing yet another panel created on the fly to prompt the user to save changes.
And so forth. It's a lot of redundant code with the wheels reinvented on a continual basis.
[This is still nothing compared to the ungodly mess the Carbon/toolbox programmer goes through. Ed.]
With NeXTSTEP most of that is gone forever. Although a Cocoa document-based application can find out the names of the files it's editing, most times it won't even bother: that's all taken care of by the Cocoa frameworks.
In fact most things are taken care of by the frameworks - the developer can instead concentrate on writing the guts of the application logic.
The ADC tools automatically generate a template for document-based applications, with the menu in place and ready to go. The hookups for starting new files, opening existing files, and saving files are already in place. The developer doesn't even have to worry about creating windows for these files.
Cocoa document based applications are run by a derivative of NSDocument. One does not often subclass in Cocoa, but this is one case where one does. The developer chooses an appropriate name, most often in Interface Builder, and has the templates for this class code automatically generated in the project.
The template code includes two hooks the developer will use to manage data flow. The comments provided give the developer all the necessary clues to figure out how to proceed.
- (NSData *)dataRepresentationOfType:(NSString *)aType
// Insert code here to write your document from the given data.
// You can also choose to override -fileWrapperRepresentationOfType:
// or -writeToFile:ofType: instead.
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
// Insert code here to read your document from the given data.
// You can also choose to override -loadFileWrapperRepresentation:ofType:
// or -readFromFile:ofType: instead.
dataRepresentationOfType: is what the application must reply to so the frameworks can save something to disk. The stream returned is of the form NSData, in other words a generic data flow.
loadDataRepresentation: is called in the application when the frameworks have a file to be opened. The stream provided is again of the form NSData.
But as the comments provided in the templates point out, there are other ways to skin the cat.
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)docType
- (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)docType
With these two methods the application sees the actual file names; the difference is they won't have to be retained. Once the methods are through with opening or saving a file, the information is discarded - it's no longer needed.
Using these latter two methods does have an advantage: you can immediately interpret the data in whatever format you see fit.
For example, if you're expecting an XML file or a property list - in other words a 'dictionary' - you can get the data directly by using the NSDictionary class. You don't have to bother with the tedium of actually opening and closing the file yourself: again, that's all done for you by the frameworks.
+ (id)dictionaryWithContentsOfFile:(NSString *)path
All you do is pass the fileName string from the call to readFromFile:OfType: and you get back your dictionary.
And because this is a 'class' method, you don't have to first allocate an instance or anything like that - the only code line you basically need is the one cited above.
Saving your file is just as easy.
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
When the frameworks call your writeToFile:ofType: method you pass the filename and it's done.
Everything else is done for you; all you have to do is write the application model.
The red button in the upper left signifying the file needs saving: that's done for you by the frameworks. The user prompt when a file needs saving: that's done for you by the frameworks. Generating new windows and windows for documents the user wants to open: that's done for you by the frameworks.
Your 'Open Recent' submenu on the File menu: that's done for you by the frameworks. Even saving the paths to your documents in your application preferences file is done for you by the frameworks.
Even the enabling of your 'Save', 'Save As', and 'Save All' menu items on the File menu: that's done for you by the frameworks.
And so forth.
The team at NeXT created a streamlined and extremely efficient framework for everyday application development. It's time the rest of the world caught on.