I’m apparently very much behind on this, but Chris Forsythe, project manager for the open-source gems Adium X and Growl has been posting weekly Cocoa challenges for the past few weeks. Unlike competitions like Ironcoder, these challenges are aimed towards beginners looking to brush up on the Cocoa frameworks. Mr. Forsythe himself is learning Cocoa and is hoping that these challenges will not only help others, but increase his own understanding of the Cocoa frameworks. Ever week, Mr. Forsythe posts a small Cocoa application with a list of classes used to create it. The challenge is to then re-implement the application using those classes.
(With all of the links in that last paragraph, this is going to look like blog spam. In a way, it is. Linking to others is a good way to increase traffic to a blog, and this blog certainly could use the traffic. However, I do like to start my posts with an introductory paragraph or two laying out the background, and often this calls for many links. I don’t put too links many in the later paragraphs, except to specific articles for the purposes of backing up my claims. Unfortunately, I’m going to throw a few more links at you.)
I was alerted to the presence of these challenges by the Late Night Cocoa podcast. I have been programming in Objective-C for over a year now, but had made minimal use of the Cocoa frameworks, instead doing most things the C way. I have delved into Cocoa some, but the sheer size of the framework is daunting, and my spare time is limited. This seemed like a good way to slowly but surely learn the ins and outs of the Cocoa framework. Unfortunately, I have missed the first two weeks of the challenge and have some catching up to do. The solution to the third challenge is due to be posted soon, along with the fourth challenge. I figure that if I can do two challenges a week, then I shall finish the fourth in time to participate in the fifth. So without further exposition, here are my solutions to the first two challenges.
Week 1: TrackingButtons
Download here.
The first challenge was relatively simple. The application contains four buttons, with titles corresponding to various shipping companies, which, when clicked, open your default Web browser to the package tracking page for that company. When the user closes the window, the application exits.
There are two obvious ways to do this. One could either create a method in the application’s controller for each button, or create a single method to open the page based on the title of the button. However, since this was such a simple challenge, I wanted to come up with a more unique way of implementing the application.
I looked through NSButton’s documentation for any NSStrings I could use to hold the URL of the page. The only two NSString fields in NSButton were the title and the alternate title. I couldn’t use the title, so I decided to try the alternate title. For some types of buttons, the alternate title is displayed when the button is pressed. It may also be used for other purposes, but I could not find any documentation to that effect. Because I used this field for something other than its intended purposes, it may have unintended side effects, either now or in the future. I would never dream of doing anything like this in production code, but I thought it was a rather unique solution to a simple (by design) challenge.
It is possible that someone else has already done the same thing. In that case, I relinquish all claims of uniqueness. However, this solution does have two benefits beyond the traditional approach. First, the size of the code is extremely small. The controller contains only two methods, each only one line long. One is responsible for opening the Web pages, and the other quitting the application. Second, the URLs of the Web sites are connected to the Buttons through Interface Builder. Any user could change the URL connected to a button, as well as the title of the button, or even add a new button, through Interface Builder.
Of course, I could have made a custom subclass of NSButton with a field dedicated to the URL, and created a custom Interface Builder Palette with it, but it seemed a little much for what is intended to be a simple challenge.
Week 2: iTunesController
Download here.
With this challenge, I did not want to use the same gimmick twice, so I did this one by the book. One thing I noticed that I did by habit was to avoid naming temporary objects which would be automatically released. This is an optimization I picked up from C++ programming. By avoiding aiming temporary objects, some compilers can preform an optimization called Return Value Optimization. I wouldn’t be surprised if the objective-c compiler does not do this, but I left it in anyway.
Also of note is the AppleScript used. My intention was to avoid an error being displayed at all costs. In that regard, my resulting script was very similar to Mr. Forsythe’s. It first checks if iTunes is running, then attempts to change the track within a try block. However, we used a different method for checking whether iTunes was running. I am tempted to proclaim that my method is more efficient, as the code is shorter, and while in the worst case, it has to check every process in the list, Mr. Forsythe’s code must check every process in the list.
Mr. Forsythe’s code also reminded me that global constants can be created using the preprocessor, and thus do not need to be an instance variable. It’s a basic technique that all to often I think of last.