So, you've read the previous four cocoa articles and you have a bunch of answers for Trivial Pursuit: Cocoa Edition. But actually getting going with an application can be daunting.
In part this was, for me, because Cocoa idioms are quite different from those of frameworks I've used before. Cocoa has quite a lot of time-saving "magic", but it's not always obvious what and where that's going on; "where I come from," the infrastructure is more overt (but less useful). In most of those, the first order of the day was to create a new subclass of Application and start filling in the blanks.
In Cocoa, many of the framework classes are designed to be used off-the-shelf without subclassing, including the Application class (NSApplication — all Cocoa classes start with NS, but I may omit the NS when referring to classes casually). You can subclass most of them if you need to change some behaviour, but this tends to be for rarer special cases, not the main jumping-off point for writing your app. Also, a lot more of the setup work is done using tools, inside Interface Builder, rather than code.
So, where do you start?
Let's say you fire up XCode, and start a new project. You'll be offered a huge range of templates to get going. Stick with a basic Cocoa Application (or Cocoa-Python Application if you're working in Python). Ignore all the Core Data Document Based Plug In Nuclear Fusion Mousetrap Inversion Field Application templates for now: let's limit how much of the API we need to learn at once.
What you're likely to do first, is open up MainMenu.xib and start laying out the main window. This is easy, and gives you something visual fast, but then you need to start hooking it up to real code.
Let's look at the files lying around your project. I like Python and don't feel like teaching Objective-C right now, so I'm going to go through the Python Cocoa Application template (the Obj-C one is very similar, though):
(Files with YourAppNameHere change their name to match the one you gave when you created the project.)
First off, these two files you can largely ignore:
These, you'll be working with occasionally:
When it first sees the app, OS X looks in Info.plist to find out about the app's title and icon. OS X looks in Info.plist to discover what file-types it can load. Some of the data is also used by Cocoa after your app launches (in particular to configure Document-based apps' open/save dialogs). These items, along with copyright notices, version numbers, and so on, make up the bulk of Info.plist.
Typically you'll set all this up when you first start out, and only make occasional changes as you add support for new file-types.
However, you will need to edit this file from time to time: Any custom classes you create, and use in Nib files, you must load here, so Cocoa can find them when it gets round to loading the Nibs. This just means you need to import the modules that contain them; PyObjC does the rest (this is a PyObjC-specific quirk: in pure Objective-C, it's taken care of by the language runtime).
If you forget, Cocoa will fall back to an ancestor class (eg NSView instead of MyFancyView) and spit a warning out to the console.
This, you may have already jumped ahead and started fiddling with:
This is the first of the files you're likely to work with regularly:
The App Delegate is responsible for answering questions like, "When the last window closes, should the application quit?" or "Does the user want to save their work before quitting?" It also receives messages (remember: method calls, not posts to the event-loop) when the application finishes loading, gains/loses focus, is hidden/unhidden and so forth. It's also responsible for tasks such as opening files.
Delegates, like most objects in Cocoa, are typically created and connected-up by the Nib. So the app delegate class from the template contains very little code; just enough to declare the class, but nothing that does any instantiation, no singleton infrastructure or anything like that.
Because Objective-C messages (as we discussed before) don't rely on C++-style virtual methods, the delegate need not even inherit from any particular class or interface. In our Cocoa-Python Application template, it just inherits from the base class, NSObject. This also means one object can be the delegate for several objects, even of different types; although, taken to extremes, this can lead to code which is confusing to read.
To find the instance itself, you'll need to go back and have a look in MainMenu.xib — you'll find one sitting around, looking like a big blue cube.
You'll also find a proxy object representing the application; it should have a delegate Outlet that is already pointing to your delegate instance. You can see this if you right-click on it.
So to sum up the startup process:
Now, if you wish, you can write a lot of your application's "guts" in the delegate: Make a button in IB, write an Action to handle it in your delegate, connect them together inside IB, and it should all Just Work. Use threads or NSTimers to do background processing. Fill in methods for delegate callbacks. That's an application right there.
For anything but the most trivial example, though, I recommend reserving the App Delegate just for handling its "official duties", and creating your own unique Controller to run the application itself. More on this in a moment.
I'm not going to get into this in-depth, but it's probably worth mentioning localisation here. Inside the project you'll find English.lproj, a Language Project. Each localisation your application contains, has its own .lproj. It's really just a folder, though; any localised resources are placed inside it, and Cocoa ensures they are loaded from the correct .lproj for the user's preferences.
Nibs and string tables are the file-types most often found here. One thing is slightly confusing: In XCode, localised files are lifted out of the lproj file and displayed separately. For instance, MainMenu.xib and InfoPlist.strings are both inside English.lproj if you look in the folders on disk, but XCode shows them at the top level alongside everything else, and each language its localised into is listed as a child of the resource, not the other way round as on disk.
Incidentally, InfoPlist.strings just localises the human-readable strings from Info.plist so that your product name, copyright notice, etc get localised. Although not everyone things this is a good idea.
There are a lot of these in Cocoa. You'll make some yourself, and use others off-the-shelf (those will get an article of their own).
You'll typically make one to host the guts of the application (the "business logic" as Java Enterprise people like to say). Just create a new .py file:
from Foundation import *
from AppKit import *
class YourAppNameHereController(NSObject):
@IBAction
def myFirstAction_(self, sender):
# Do stuff here
pass
Save it, and if you're working in a third-party editor, make sure it's added to the XCode project in the Classes section (just drag-and-drop it in). You don't need to build the project just yet, just go straight to IB, and you should be able to now make an instance of YourAppNameHereController. It won't do anything yet, but you can start filling the Python class with the code you need, expose it with the @IBAction decorator, add Outlets by creating member variables (eg mainWindow = IBOutlet()), and then hook it all up in IB. Now import YourAppNameHereController in main.py and build your project: It should all Just Work(tm).
You may be tempted to get busy writing initialisation code in the constructor. This way, pain lies. This is because you cannot guarantee which order the objects in your Nib are created. Don't go there. Instead, there are two good ways to handle init, depending on your exact requirements:
As you may be getting used to by now, Document-based applications do this slightly differently. I'll be doing a separate article on those some day. Hopefully you have enough to find your way around now, at least with some basic controls.