Bloggiversary

June 26th, 2006

D’oh! My blog turned one year old this past Saturday, and I was so thoughtless I didn’t even send it a card.

Happy Birthday, Red Sweater Blog!

It’s been an interesting year. A place to speak one’s mind is a powerful tool, and I’m grateful to the internet for that. The 150 entries I’ve made during this first year must add up to at least 300 hours of time dedicated to researching, writing, editing, and responding to comments. It boggles the mind to consider it all in one retrospective lump, but I cry not over those well-spent hours.

I’ve met a lot of really interesting people through the endeavor, and the process of sharing technical facts and opinions helps to stimulate the ongoing development of my own thinking and working process.

All in all, I think it’s a win-win for writer and readers alike. Thanks for spending some of your valuable time here on my little piece of the internet.

Minimal Scriptability

June 26th, 2006

The Cocoa Scripting layer makes it extremely easy to expose manipulation of your application’s model to users via AppleScript. That said, for most developers the learning curve associated with this can be far from “easy.” The psychological burden of tackling scripting support seems to prevent many developers, even those within Apple, from providing even the barest of scripting support for their applications.

The promise of Cocoa Scripting is essentially that some robust AppleEvent parsing code in the Cocoa frameworks will translate the “yucky, raw events” into method dispatches that “just work” with your application’s attribute-based data model. Then, a different chunk of robust code will take the Cocoa objects that your application works with and automatically translate them into suitable AppleScript representations. For instance if you expose an NSString attribute from your application’s data model, the text contents are automatically converted to an AppleScript-native format suitable to the scripter’s needs. Similar bridging is done for objects as complex as windows and documents.

The power of this adaptive layer can be witnessed by taking an otherwise unscriptable application and “throwing the switch” that gives Cocoa permission to take over scripting. If you’ve got an unscriptable Cocoa-based application, why not use this as an opportunity to get your feet wet? In fact, Apple ships a good case study for this in Preview, so I’ll use that as a guinea pig while you follow along with whatever app you like. (thanks to John Gruber for pointing out this shortcoming and inspiring this article). Take the following script and paste it into Script Editor:

tell application "Preview"
	get name of document 1
end tell

When you run the script, even if you can see an open document in Preview, you’ll be met with a rather rude reply: “Can’t get name of document 1.” Please! You’re telling me you can’t even offer me that tiny glimpse into the application’s personal details? Unfortunately Apple didn’t “flip the switch” on Preview, so none of the easy freebies from Cocoa Scripting are exposed through it.

So let’s flip the switch for them. Let’s pretend we work for Apple in the Preview group and our manager has finally given us the go ahead to investigate AppleScript support for Preview. Heck, let’s assume we’re the new intern, it’s day one, and we haven’t even been given source code access yet!

By navigating into the Preview.app package and opening the Info.plist document in Property List Editor (or an editor of your choice), we can add the requisite flag that instructs Cocoa Scripting to take over:

Now quit and relaunch both Preview and the Script Editor. Paste your example “name of document 1” script again and observe that the frontmost document’s name is returned!

Now you may be thinking, “Who cares? It’s just the title of the document, that’s not very useful.” But that’s not for you to decide! Let the script author decide. They’re the ones who have to use your program to get something done. The big deal here is that we turned an unscriptable application into a “scriptable beta” with the addition of a single Info.plist attribute. We didn’t even have to add a scripting dictionary!

Lest you think that the standard Cocoa Scripting functionality is only the name of the document, I will share a slightly more complex (albeit still contrived) example. Let’s suppose we have a hundred documents to review by hand with Preview. Based on some human-observable trait we want to open 25% of those documents later in Safari for printing or whatever. One option would be to go through and save a copy of each such document to a separate directory. When we’re done with the grueling day of work, we’ll drag the contents of that separate directory to Safari (or whatever application).

Using AppleScript and the “newly scriptable” Preview application, we can simplify this workflow by making the assumption that “any document with a tiny window” should be opened in Safari. Now as we go through the open documents one by one, we simply drag-resize the window to be less than 400 pixels wide. Later on, we run the following script, which makes a list of all such windows and opens the corresponding documents in Safari:

All of this for free! We make a kick-ass intern! And all on our first day with no source code.

By no means am I suggesting that developers should just flip the switch and ship. It’s very frustrating to see unscriptable apps, but even worse when a product claims scriptability but for all practical purposes is not. The point of this entry is merely to point out that Cocoa gives us a huge head start towards a decent scripting implementation, and to choose not to take advantage of that is cruel and unusual punishment to your users.

Just think how cool Preview will be when we add a dictionary that describes its document object model’s custom attributes.

Summer Xcode Tips

June 22nd, 2006

It’s summer! The sun is out and the sky is (leans right to peek outside), well, sort of greyish. While the world celebrates the changing seasons, we’re all leaning over our keyboards and squinting into the screen, working on our LCD tans. All this warm weather puts me in the mood for Xcode tips! I’ve been saving up a few “gotta share” revelations. Are these tips no-brainers? Maybe for some of you. But it took me a while to “get it” and I’m hoping some of you will also giggle with glee when you learn out about them.

Hint #1: Double-Click to Open in External Editor

As my projects become more complex, they inevitably become the gathering place for an assortment of file types that support the production of and testing of the final product. Most of these files are source files, and Xcode is good at editing them. But others, such as scripting definitions and carbon resource files, are best left to external editors. Unfortunately, there are a number of file types that Xcode thinks it’s a good editor for, when it really isn’t. Even some source files might be better edited by external applications, depending on the particular goal.

I’ve spent a lot of time right-clicking such files and selecting “Open in Finder” from the contextual menu. This has the desired effect of opening the file in whatever application you’ve specified as the file’s owner via the Finder. That’s not a bad solution, but it’s slightly annoying, and not nearly as streamlined as double-clicking. But when I double-click a typical file that Xcode claims understanding of, it opens in a separate Xcode editor window. Ugh! I wanted to open in the external application!

From the “File Info” window in Xcode, you can tell Xcode what type you want it to consider the file to be. This is handy for things like making it apply Objective-C++ rules to a “.m” file, but the list of file types it’s prepared to acknowledge is really vast. As I set out to convince Xcode to open my file with another app, I thought I surely needed to teach it more specifically what kind of file it is. It’s not a text file, it’s an XML text file. No dice. Xcode “handles” that, too.

The answer to this problem is surprisingly simple, but it requires counter-intuitive (to me) reasoning. Instead of being more specific about the file’s type, you have to be less specific. Among the myriad choices is an item called simply “file.” When a file’s type is “file,” what do you know, it opens up with the Finder when you double-click it.

Note, if Xcode has already “laid claim” to being editor of your file, it probably has an open file reference to it somewhere. It will not give up its role as editor until you close the file. You can do this by choose “Close File [Blah]” from the File menu while the file is focused, or by pressing the shortcut Cmd-Shift-W.

Hint #2: Eliminate Build Configuration Strain

Since Apple announced the transition to Intel, quite frankly the hardest development aspect for me has been keeping all the darned build settings straight. If you are willing to target 10.4 and later, things are pretty straight-forward. But if you live in the real world and need to continue supporting older release of Mac OS X, things get trickier. To support earlier than 10.3.9 and Intel in a single universal binary, you have to use separate compilers at a minimum, and probably separate SDKs as well.

If you’ve had to do this for one or more of your projects, you probably got used to locating, copying and pasting the build settings from one project to another. Yuck! Not only is this prone to error, but it is “stupid work” that takes up a lot of time and energy. Apple’s answer to this problem is the “.xcconfig” file, which allows you to specify a number of baselines build settings outside the scope of a build configuration.

These “.xcconfig” files are simple text files containing variable names and their desired values. For instance, here are the contents of a baseline file I use for “Universal builds that need to run on 10.2”:

ARCHS = ppc i386
SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk
SDKROOT_ppc = /Developer/SDKs/MacOSX10.3.9.sdk
GCC_VERSION_i386 = 4.0
GCC_VERSION_ppc = 3.3
MACOSX_DEPLOYMENT_TARGET = 10.2

I call this file “Universal 10.2+” and keep it in a common location accessible to all of my projects. Now when a particular build configuration in any project needs to “inherit” these settings, I use the “based on” feature of Xcode’s build configuration system. You simply drag a reference to the “.xcconfig” file to your project, whereupon it becomes available in the appropriate popup menu:

Notice that in this case I’ve chosen to name the “inheriting” build configuration “Release 10.2+”. There is a method to this madness: by adopting a naming convention for build configurations that match the system requirements, I can easily ensure that libraries from dependent projects get built under the same settings. By using a consistent naming scheme, and pointing all such build configurations at the same “based on” configuration file, I can rest assured that changing the settings in one place will percolate out to all the affected targets.

Also notice that “Architectures” is not even set by the project (it would be bold if it were). The setting of “ppc intel” comes from the configuration file. The project yields responsibility for defining what “Universal” means. So if Apple adds the rumored support for the Z-80 processor, I’ll only have to change the settings in my “.xcconfig” file.

I’ve barely scratched the surfaces of these files’ power. On top of the “based on” notion from Xcode, the files themselves can also explicitly include other files to stack a great deal of common build-setting definition into your process before the project-specified configurations are even reached.

Hint #3: Install a Directory of Changing Supporting Files

Let’s say you’ve written an application with an awesome templating system. You allow your customers to write their own templates and use them as the basis for new documents. To get your users off to a good start, you include a bunch of built-in templates in your app. In fact, your system is so good that there are literally hundreds of built-in templates and you’re adding more in every release.

All you want Xcode to do is put your directory full of “Built-In Templates” into the resulting application’s “Resources” directory. If you’re like me the first thing you think of doing is simply adding the directory of templates to Xcode, and then dragging the resulting folder icon to the “Install Bundle Resource” build phase. After all, that’s what this standard build phase is for, right? Yes. But the standard build phase does something annoying. Instead of copying the directory to the resources folder, it copies each individual file over to the root of the resources directory. This makes for a very cluttered resources directory.

The good news is that a simple “Copy Files” build phase of our own can get the job the proper way. Just add a custom build phase to your target with target directory “Resources”, and copy your folder reference to that. For some reason the built-in resources phase is “smarter” than it perhaps should be.

Note: When you add your directory filled with templates to the Xcode project, make sure to check the box that requests “folder references” be created. This is how you tell Xcode to take the folder as it is at any moment. Instead of adding the contents of the folder to the project as a group, it will dynamically refer to the contents of the folder as they are at any time. Now when you add new templates to the folder, they’re automatically copied into the app’s resource folder when you next build.

What a Difference a Cell Makes

June 17th, 2006

I’ve been bothered for a long time by a limitation of NSTextFieldCell that prevents developers from specifying a vertically centered alignment. This is particular annoying in contexts where Apple practically rolls out the red carpet for a particular UI feature, but where taking advantage of that feature is almost guaranteed to produce an ugly result. Take NSTableView for example. What is the most common cell type in a table? NSTextFieldCell of course. But what happens when you add the slightest bit of creativity to a table, say by changing the row height to a value not suited to the font size?

That’s bad enough, but let’s say you find good reason to reach for even taller table columns? It can only get worse from here:

In the past I’ve just worked around this problem by finding another way to present my UI. In other words: just keep the freakin’ table view rows shallow, or else live with the funny looking top-alignment of the text. Today I got fed up with that and decided to shoot for a general-purpose solution.

I didn’t want to implement my own completely custom text cell. NSTextFieldCell is pretty good, you know! It does a lot for me, and I’d like to continue taking advantage of that. Through a bit of experimentation, I discovered that I could make a pretty minor tweak to NSTextFieldCell and get the behavior I wanted. By overriding “drawingRectForBounds:” and returning, instead of the whole lot of real estate available to me, a centered subset of that area, I convinced AppKit to render the text in the middle like I wanted it:

Hey! That’s looking a lot better. I like it. It looks pretty. You know, like a Mac. But you know what doesn’t look pretty? When I double-click this bad boy and try to edit the text:

Ouch! That’s a pretty freaky looking user experience. Apparently NSTextFieldCell or some other component of AppKit is not used to having the drawing rect drastically adjusted without its knowledge. I might have to call this whole deal off. However bad top-aligned text in my table cell is, it’s a bajillion times less offensive than that. But then I realized that as a cell, I am given a lot of control over the edit process. What if I can limit my rect adjustment so that it’s normal while we’re editing, but weird (i.e. good) all the rest of the time?

I added two methods to my custom subclass, overriding “selectWithFrame:…” and “editWithFrame:…”. In these methods, I use my rect calculation method to transform the proposed edit or selection rect into the more desirable “centered rect,” then I set a flag in the class “mIsEditingOrSelecting” which tells my rect calculator to stop the funny business. The result is an NSTextFieldCell with very little code that, as far as I can tell, behaves appropriately in all circumstances:

Good enough for beta software! But here’s where you come in. You want this sexy vertically aligned cell for your app, right? So let’s share and figure out whether it’s monumentally busted or not.

Download RSVerticallyCenteredTextFieldCell. MIT License. Pop it into your project and, upon waking from nib, set your text column data cells (or other text field cells, if you have good reason) to instances of it. Let me know if you spot any bugs!