Preprocessing InfoPlist.strings files in Xcode

Posted on September 3, 2009 by Brian Webster
Filed Under Cocoa, Development | Comments Off

Xcode provides a handy feature called “Info.plist preprocessing” that allows you to specify placeholder values in your plist that are then automatically replaced by Xcode with values you specify in your build settings. The most common use of this is to place the current build’s version number in one or more appropriate places in the Info.plist file. This lets you change these kinds of values in one place, rather than having to track down every usage in the Info.plist and replacing them manually.

In addition to the Info.plist file itself, you can also have one or more localized InfoPlist.strings files included in your application bundle. There are some strings in the Info.plist (e.g. CFBundleGetInfoString) which are displayed directly to the user, and thus need to be localized. Putting these keys in InfoPlist.strings instead of directly into Info.plist allows you to have localized versions of these strings.

However, Xcode’s Info.plist preprocessing only covers the actual Info.plist file, and not the various InfoPlist.strings files you may have included in your application bundle. This means that if you have a version number or some such that you want to keep up to date in one of these strings, you have to resort to replacing them manually, which is both error prone and a pain in the ass. I was able to solve this problem for my own project, and thought I’d share the details here in case it came in useful to anyone (and in case I forgot how to set it up in a future project and needed some help from my past self :) )

To get things working, I enlisted the help of a neat little tool called wincent-strings-util, courtesy of Wincent Colaiuta. It performs several tasks, but one of them is specifically geared towards doing this kind of substitution in a .strings file. In my setup, I simply include a copy of the tool in my project repository, so no installation is required and the build “just works” wherever you check out the project.

To get the substitution to be done when doing a build in Xcode, I defined a new build rule in my target to use a custom script to handle Info.plist files. To do this:

  1. Choose “Edit Active Target” from the Project menu, click on the “Rules” tab, then click the “+” button down at the bottom of the window to add a new build rule.
  2. From the “Process:” pop-up menu, select “Source files with names matching:” and then “*/InfoPlist.strings” in the field next to the pop-up. When I first tried this, I entered simply “InfoPlist.strings”, but that didn’t work because the match is against the whole path of the file, and not just the filename, so the * is necessary to match any file named “InfoPlist.strings”.
  3. From the “using:” pop-up menu, select “Custom script:”, then enter the following script text:

    cd "${BUILT_PRODUCTS_DIR}" && ${SRCROOT}/DevFiles/wincent-strings-util --info "${INFOPLIST_PATH}" --strings ${INPUT_FILE_PATH} --output /tmp/${INPUT_FILE_NAME} --encode UTF-16LE

  4. Where it says “With output files”, click the “+” button and enter “/tmp/${INPUT_FILE_NAME}” for the output filename

You can click here for a screenshot showing the completed build rule setup. A couple things to note:

Finally, to actually do a substitution, simply put a key from your Info.plist into the InfoPlist.strings file, enclosed in chevron brackets («»). For example, I have the following entry in my InfoPlist.strings file:

“CFBundleGetInfoString” = “PlistEdit Pro version «CFBundleShortVersionString», Copyright 2004-2009 Fat Cat Software.”;

This will replace the «CFBundleShortVersionString» placeholder with the value stored under CFBundleShortVersionString in the main Info.plist file. I have my Info.plist preprocessing already set up to include the version number of the build for CFBundleShortVersionString, so this will propagate that to the InfoPlist.strings files, and my version number will display correctly in the Get Info window for all localizations.

How to create a fake digital camera for Image Capture

Posted on December 5, 2008 by Brian Webster
Filed Under Development, iPhoto | 1 Comment

I’ve been fooling around with some Image Capture APIs lately, but the only convenient camera device I have to work with is my iPhone, which doesn’t really have any way to put specific images onto it if I want to test things out like different formats, movies, etc. After digging around with the Image Capture SDK a bit, though, I’ve found a simple way to make a fake “camera” that Image Capture will recognize, but doesn’t require plugging in a physical device.

The trick is to create a disk image that, when mounted, Image Capture believes to be a digital memory card, and treats as a digital camera, so it shows up in iPhoto, Image Capture.app, and so forth. There appear to be a bunch of different ways to do this, but here’s the method I used that worked for me:

  1. Open Disk Utility and click the “New Image” button in the toolbar. In the configuration sheet, choose “MS-DOS (FAT)” for the Volume Format, and “Single Partition – Master Boot Record Partition Map” for the Partitions setting. You can enter whatever name and volume size you like, then click “Create”.
  2. camera-image-setup.png

  3. Once the image is created and mounted, open it up in the Finder. Create a new folder at the root level of the mounted disk image and name it “CAMERA”. Then, go ahead and copy whatever pictures or other items you want to test with inside the CAMERA folder. This appears to be one particular camera’s way of storing photos on its memory card – there are a whole bunch of others (”DCIM”, “Photos”, “Videos”, etc.) that would work equally well.
  4. Click the eject button in the Finder, then double click the image file to mount the image again. If everything is set up correctly, the act of mounting the image should cause Image Capture to fire up whatever application you have set to handle your digital cameras (e.g. iPhoto), and the images you put in the CAMERA folder should appear in the list of photos for the device.

Not only does this save having to plug and unplug a device whenever you’re doing testing with Image Capture, but it’s also really nice for test suites, since you can just include the disk image as data for your tests, and then mount the image to simulate plugging in a camera.

Why I’m not developing iPhone applications

Posted on September 12, 2008 by Brian Webster
Filed Under Development | 4 Comments

When the iPhone SDK was announced earlier this year, I downloaded it right away and was very excited to see all the potential stuff that could be done. That initial excitement has slowly given way to the full realization of the terms dictated by Apple for distributing apps.

The first restriction is, of course, that you must distribute your application through Apple’s App Store and iTunes, and Apple gets a 30% cut for their efforts. That part actually isn’t totally bad, since you do get a lot of benefit for being listed on the store without having to deal with any of the setup or maintenance. Sole distribution through iTunes helps with the integration that gives the iPhone such a good user experience, and 30% isn’t an unreasonable cut for Apple to earn on each sale.

The real problem though is the fact that Apple is the sole arbiter of what applications get accepted and rejected from being listed on the App Store. This would be somewhat bad if there were a concrete set of guidelines that said what was and wasn’t acceptable for inclusion. There are some rules outlined in the SDK agreement, but there are apparently also a lot of other “unwritten” rules that are governing Apple’s decision on whether to accept a specific application. Apps have been denied/pulled for UI compliance reasons, for resource usage, for “taste” (e.g. the “Pull My Finger” app), and for no particular reason other than some vague assertion of “usefulness” (e.g. the “I Am Rich” app which cost $999 and did nothing).

The latest app to be rejected takes things over the top, though. Podcaster, an iPhone app designed by Almerica, was rejected by Apple because it “duplicates the functionality of the Podcast section of iTunes”. Forget the fact that this isn’t even functionality that’s only on the desktop application, iTunes. What Apple’s saying here is that they can claim exclusive rights to ship applications for certain domains of functionality, and reject any third party application that tries to compete in that area.

Want to make an e-mail application for the iPhone? Too bad! Apple’s already got one, so you can’t duplicate that functionality. Music player? Nope, sorry, can’t do that either. Or what if, for example, you already have an application selling on the App Store, and then in a software update, Apple decides they want to offer similar functionality. Can they then boot your app from the store, since it’s duplicating what Apple’s done? And what does this do for the power of competition among iPhone applications? It’s a very chilling effect.

Now, it’s perfectly possible that this was a one-time screw up by one Apple reviewer. I really really hope it is, and that Apple clears this up as soon as possible. But regardless of what happens in this specific instance, the real problem here is that there is no clear and definitive list of rules for what will and won’t be accepted to the App Store. Even internally, Apple doesn’t seem to have one, since some apps are rejected for a particular reason, while other apps are accepted that could have just as easily been rejected for the same “offense”.

Before I even think of sinking time into developing an iPhone application, Apple needs to come out with a published (at least to developers who agreed to the SDK terms, if not fully public), detailed description of what criteria an application has to meet to be included in the App Store. Then, of course, they need to apply those guidelines consistently across all applications submitted for approval. Neither of these things is even close to being done now, and I for one am not going to invest time in developing an application without knowing beforehand whether I’ll even be able to sell the damn thing.

Per-object ordered relationships using Core Data

Posted on August 8, 2008 by Brian Webster
Filed Under Cocoa, Development, Programming | 5 Comments

Core Data is a great technology to allow easy creation of complex data models, saving you from writing a lot of boilerplate code. One of the limitations of Core Data, however, is that when one entity has a to-many relationship with another entity, the objects in that relationship are unordered. The only order that can be imposed on such a relationship is by sorting based on one or more attributes of the child objects, and there is no “inherent” ordering to the objects otherwise.

Several different solutions (one example here) have been created for this problem, most of which use some variation of adding an “index” attribute to the child objects which specifies what order the objects come in (i.e. the first object has an index of 0, the next and index of 1, and so on). The tricky part come in keeping the index attributes up to date, but there are several solutions for that which all work pretty well.

The major disadvantage to this approach comes when you have child objects which can be contained by more than one parent object. The classic example of this is a program like iTunes, where you can have multiple playlists, each of which can contain multiple tracks, and the user can arrange the tracks in any order they wish in each playlist. The problem is that there is only one “index” attribute for each track, but each track will have a different index depending on what playlist we’re looking at.

I decided to take a stab at this problem using a new approach, which stores the ordering information for each container object in the container itself, and not in the children. I also wanted this solution to be generic and reusable, so you don’t have to reimplement everything for each relatonship you want to give a custom ordering to.

The solution I came up with is a custom subclass of NSManagedObject called BWOrderedManagedObject, which provides support for imposing an ordering on any to-many relationship the object has. At the end of this post, you’ll find a download link containing the source code for this class, as well as a corresponding BWOrderedArrayController class which provides support for sorting a table view by this custom ordering, and a simple demo showing how to hook everything up.

BWOrderedManagedObject

In order to keep track of what order the objects in a particular relationship are in, we need to store a separate piece of data that provides that ordering. The way BWOrderedManagedObject handles this is to store the ordering in a separate attribute. By default, the key used for this attribute is simply the relationship key with the string “Ordering” appended to it, so for example, if you have a “tracks” relationship, the ordering info for your tracks will be stored under the “tracksOrdering” key.

The ordering attribute should be defined in your Core Data model as a transformable property attribute with the appropriate name. The attribute will actually consist of an NSArray holding NSURL objects, with each URL object containing the URIRepresentation of the NSManagedObject it represents. The URIRepresentation is obtained from the NSManagedObjectID for a given object, and is a simple, persistent identifier that can be used to identify the object and won’t change even when saved to disk and read back again (with one important exception – see below). The order of the URLs in the array is what defines the order of the objects themselves in the relationship.

To actually specify an ordering for your objects, NSManagedObject provides a set of methods that allow you to access, insert, delete, and move objects within the array (objectInOrderedValueForKey:, insertObject:inOrderedValueForKey:atIndex:, etc.). It also provides a mutableOrderedValueForKey: method (similar to Cocoa’s mutableArrayValueForKey: method) which returns an NSMutableArray proxy object which you can use to rearrange objects in the relationship as though it were a plain array. To actually access the ordered values, just use the orderedValueForKey: method, which returns an NSArray containing the objects in the correct order.

BWOrderedArrayController

In a typical NSTableView + NSArrayController setup, sorting is accomplished when the user clicks one of the table view’s column headers. Each column has an NSSortDescriptor assigned to it, which are then passed to NSArrayController, which sorts its contents based on the sort descriptors. However, since sort descriptors function by comparing attributes of the objects themselves, this method of sorting won’t work for BWOrderedManagedObject, because the sort order is stored in the container object, and not the objects being sorted

BWOrderedArrayController fills this gap by handling the case where the user has clicked a column which represents the “natural” ordering of the table’s content. To use BWOrderedArrayController, first bind its “contentSet” binding to your object’s content just like you would with a normal array controller. Then, in your table view, select the column you want the user to be able to click to sort the content by their custom ordering.

Instead of binding this column’s “value” binding like your other table columns, instead switch to the attributes inspector pane and enter the name of the ordering key that your content’s ordering is stored under in the “Sort Key” field. For example, if you’re displaying a list of tracks that’s stored in the “tracks” key of the selected playlist, you would enter “tracksOrdering” in the Sort Key field.

If everything is hooked up correctly, clicking that table column will sort the content of the table based on the ordering specified by the user. Performing actual rearrangement of items via drag and drop is not implemented in BWOrderedArrayController itself, but in the project download, an example of how to implement reordering is given in the MusicLibraryDocument class

Inner workings

Most of the nitty gritty of how the code works is documented in comments in the code itself. The tricky parts were:

Using the code

The code is freely available under the MIT license and can be downloaded from this link. The download consists of an example project which demonstrates a very very basic data model with two entities: Playlist, and Track. The Playlist entity has a to-many “tracks” relationship and a “tracksOrdering” property to contain the ordering information. The demo is a document based app that lets you create new playlists, drag files from the Finder into the track list to add them, sort the tracks in order, and rearrange the tracks in the list via drag and drop. The idea is loosely modeled after iTunes, but the demo doesn’t actually filter out non-music files, or play music, or anything else useful. It is a demo, after all. :)

The BWManagedObject class can be used as-is and has no dependencies, and BWOrderedArrayController only depends on BWManagedObject, so you should be able to just add them to your project and compile them (do make sure to link to CoreData.framework, of course).

I have not yet tested this code extensively, so use at your own risk. In particular, I have no idea yet how it performs with larger data sets or other variations in configuration. I welcome all feedback and bug reports at bwebster@fatcatsoftware.com. If I get enough interest, I may even put the code up in a public VCS of some sort, for now I’ll stick with the zip file.

The code will only work on OS X 10.5 and later, the primary reason being that transformable attributes are only supported on Leopard, which is what the ordering attribute must be defined as. This is possible to do under Tiger as well, but requires a lot more code, which I didn’t feel was worth the time to write. Apple outlines what needs to be done in their documentation though, so it should be possible to backport if you need it to work under Tiger. Most of the other code should be mostly Tiger compatible (it doesn’t use new ObjC 2.0 features, for example), but may contain other Leopard gotchas.

Fixing Applescript

Posted on June 3, 2008 by Brian Webster
Filed Under Development, Programming | 2 Comments

After reading Daniel Jalkut’s post about replacing/supplementing Applescript on the Mac with Javascript (or perhaps another scripting language such a Ruby or Python), it got me to thinking about what exactly it is about Applescript that tends to trip people up. Would it be possible to figure out what the problem areas are, and just fix Applescript in those areas? There would be problems to this approach, the most prominent one being backwards compatibility. Some of the problems are fundamental enough that, if you fixed them, it would cause existing scripts to break. I believe Apple actually had plans a few years back of making a “new” Applescript that mostly acted like current Applescript, but was in fact a separate language and made an explicit break with previous versions of the language. Kind of like Carbon, where 80% of the stuff would work fine, but that problematic 20% would need rewriting under the new system.

So, what exactly are the problems that vex Applescripters so? There are quite a few, but here are some of the ones I can think of.

Lack of basic language features


Many modern scripting languages come with a fairly hefty set of built in data types and functions (or classes and methods for OO languages) that support a wide array of built-in functionality. Applescript has its own set of functionality, but for many standard tasks, it frequently either lacks the ability to do so at all or supports things but in a very hard to use or unintuitive way. A few examples include:

This is just a small sample, I’m sure there are many other things that are pretty standard in other languages that are absent from Applescript, or hidden away in the Standard Additions scripting dictionary somewhere.

File references are the devil



There are at least 5 different ways I can think of to refer to a file using Applescript (alias, POSIX file (a.k.a. file URL), Finder style object specifier, POSIX path, Carbon path). Different applications use different data types in different places, and will often barf back errors if you don’t use just the right kind of file reference. Figuring out how to translate from one kind to another is often maddening. Here’s an example from one of my own scripts, which uses three of the five types in a single line:


set helpFolderPath to POSIX path of ((folder "Help" of folder "en.lproj" of ptFolderAlias) as alias)

Files really need to be treated as first class citizens, with support built in to the language, and without needing to rely on the Finder for all file system access. Or wait, am I supposed to rely on System Events now instead? Or maybe a “do shell script” call to ls on the command line? Oh, the pain.

Scripting dictionaries



This is one of the biggest hurdles that a lot of beginning scripters have with Applescript, is understanding how scripting dictionaries work. Tools like Script Debugger are extremely helpful when it comes to exploring an application’s dictionary (I hardly look at the dictionary anymore these days, and just go right to the explorer pane and start drilling down), but while $199 is a fair price for a developer, most people aren’t going to want to drop that sort of change just to learn Applescript. Script Editor did get a pretty good upgrade in Tiger, but I think it needs more work to help beginning scripters understand how to explore and use scripting dictionaries.

The death of recordability

Another invaluable tool for beginning scripters which is all but dead is application recordability. Back in the OS 9 days, you could hit the “Record” button in Script Editor and go perform operations in other applications, and the equivalent script commands would magically appear in Script Editor. This was a great way to quickly learn the commands to use to do certain things you already knew how to do using the GUI of the application. Since OS X, recording has basically gone away. The only applications I know of that are still recordable are the Finder and BBEdit/TextWrangler. Cocoa has zero support for recordability. Theoretically, you can use the Carbon APIs to do this still. I actually tried once, and gave up after a couple days of banging my head against the wall. I’d love to see recordability come back, with a whole new approach if necessary, with full Cocoa support.

Applescript does have a lot going for it, but is hamstrung by a lot of these types of issues and prevented from reaching its full potential. Replacing it with another language is one way to go, but fixing Applescript also has a lot of potential. This above list is just a sample of some of Applescript’s shortcomings (if you have your own pet peeves, feel free to express them in the comments). The idea of redesigning Applescript from the ground up, with nearly 20 years of experience to build on, is very appealing, but who knows if Apple will ever have resources to devote to a project like that.

Optimization for Dummies

Posted on November 23, 2007 by Brian Webster
Filed Under Cocoa, Development, Programming | 2 Comments

A somewhat inconspicuous looking article on optimizing third party code has stirred up quite a conversation/flame war among many in the Mac developer community. The comment thread on the article is already quite long, and it seems to me that the people with opposing viewpoints are just talking right past each other at this point, so I thought I’d add in my own perspective here.

The jist of the article is that the author, Ankur, needed to draw some gradients in one of his applications, downloaded the source code for CTGradient, an open source library that provides a class to draw various types of gradients, and decided that it had way more functionality than he needed in his own program. So, he went through the code and basically removed everything he didn’t need for this single application. That’s all fine and good, and the article is actually an interesting look at performing various refactorings and dead code removal.

The problem arises in that he implies that if a developer decides to use some open source code and doesn’t go through and strip out every ounce of functionality that they’re not immediately using, that means that they’re encouraging “code bloat” and that their code is not “optimized”. Several commenters asked what kind of performance/memory gain he actually saw, and the only numbers he provided were from the “Real Memory” column in Activity Monitor, which is a pretty crude measure. The conversation went downhill from there.

I think one main point of miscommunication here is over the terminology the original author uses for some of the things he’s talking about. When you talk about “optimizing” code, I, and most other developers I know of, think of making the code run faster. The author reinforces this by stating “…you can optimize this thing till it runs like a Ferrari”. Certainly sounds like he’s talking about making the code faster, but the vast majority of what he’s doing is simply stripping out code that he’s not going to be using. This has pretty little effect in and of itself – it saves a few KB in disk space, and if the code truly is unused, it probably won’t even get paged into memory in the first place. There are a few places where the code probably runs faster, but the gains look pretty minimal in the big picture. However, arguing the nitty gritty details about his particular optimizations is missing the bigger point…

Engineering is all about tradeoffs, and in computer software, this typically means choosing between things such as memory usage, disk usage, CPU usage, and so on. Every one of these, however, inevitably comes up against the restraint of development time. You can spend days, weeks, or months optimizing your code in various ways, but it’s all for naught if you don’t eventually ship your application. This means that you can’t do everything, and have to pick and choose what areas of your code to work on, whether to add new features or shore up existing ones, and how much time to spend optimizing performance and memory usage.

What is conspicuously missing from the article is any sort of evidence that CTGradient was actually causing any sort of performance or memory problem in his application in the first place. Now, may more have gone on that he didn’t include in his write-up, but it sounds like he simply looked at the code, decided that it was obviously too big and bloated, and set to work spending quite a bit of time hacking it down to the bare minimum he needed.

Finding and fixing performance and memory problems in real applications, however, is rarely so simple. It’s rare that you can glance at a piece of code and immediately deduce that it’s going to be problematic for your application, causing slowdowns or whatnot. Most such bottlenecks are discovered as a result of rigorous testing, using tools provided by Apple such as Sampler, Shark, and Instruments (among others) to dig into the details of what your app is actually doing and where it’s spending its time. Upon discovering such a problem, you can then go in and spend your precious time fixing what most needs to be fixed. It’s not that the modifications he makes don’t actually reduce code size and memory footprint (they do) or increase performance (still not really sure about this without empirical data), but with testing first to find what needs fixing, the time spent doing all this could very well have been better spent fixing something that actually needs fixing.

I actually use CTGradient in a couple of my projects, and I use the code basically untouched. This is not because I’m “lazy” and would rather count my customers’ money while cackling evilly than spend the time to strip out everything I don’t use, but rather because I have plenty of other things to spend time on, optimizing my app in ways that make a difference, and adding features that people want and need. None of my tests of my drawing code have ever shown any performance problems arising from using CTGradient as-is, so my motto is, if it ain’t broke, don’t fix it. The critical flaw in Ankur’s argument in his post is that he never showed any evidence that anything was broken in the first place.

Miscellaneous Leopard development gotchas

Posted on November 1, 2007 by Brian Webster
Filed Under Cocoa, Development, Programming, Tips & Tricks | Comments Off

I’ve been using Xcode 3.0 under Leopard to do my development since Leopard came out last week, and thought I’d share a few changes that tripped me up.

Well, that’s what I’ve got for now, I may add more to the list later as I come across them. Hopefully this will help somebody out there (or maybe myself in a couple months when I forget and have to relearn this stuff over again).

Running FogBugz on Leopard

Posted on October 30, 2007 by Brian Webster
Filed Under Development, Tips & Tricks | 2 Comments

I use FogBugz as my main bug tracking and customer support system. It’s a neat application that runs on PHP and a web interface and allows me to both keep track of bugs/features in my development, as well as handle all tech support e-mail. It’s designed with a multi-user environment in mind, but works quite well for a one-man shop such as myself. The web interface is not as good as a true desktop interface could be, but it’s perfectly sufficient, and I’ve become quite dependent on FogBugz as part of my daily workflow.

Dependent enough that it was a rather rude (but not wholly unexpected) discovery to find that upgrading to Leopard pretty soundly hosed my FogBugz installation. Not that it went and deleted files or data or anything, but the setup was quite far from working normally.

The primary cause for this was that Leopard now ships with Apache 2.2, whereas Tiger came with Apache 1.3. All the various differences are well documented out there on the intertubes, but a lot of things, such as where configuration files are stored, changed between the two versions. For example, instead of httpd.conf being in /etc/httpd, it now resides in /etc/apache2.

I got that part figured out pretty quickly, but then when I went to start up the web server, I would get a message printed in the system console reading:

Oct 30 12:07:57 bw-mbp org.apache.httpd[11317]: httpd: Syntax error on line 484 of /private/etc/apache2/httpd.conf: Syntax error on line 8 of /private/etc/apache2/other/+entropy-php.conf: Cannot load /usr/local/php5/libphp5.so into server: dlopen(/usr/local/php5/libphp5.so, 10): no suitable image found. Did find:\n\t/usr/local/php5/libphp5.so: no matching architecture in universal wrapper

Hmmm, well, I do have a custom build of PHP5 (in /usr/local/php5, with additional extensions needed for FogBugz) and it seems to be finding that OK, but it’s complaining about architecture something-or-other. I’m still running on Intel, aren’t I?

[bw-mbp:~] bwebster% file /usr/local/php5/libphp5.so
/usr/local/php5/libphp5.so: Mach-O universal binary with 2 architectures
/usr/local/php5/libphp5.so (for architecture ppc): Mach-O bundle ppc
/usr/local/php5/libphp5.so (for architecture i386): Mach-O bundle i386

Yeah, that’s got an Intel build in there, that’s OK. What the heck is Apache’s problem?

[bw-mbp:~] bwebster% file /usr/sbin/httpd
/usr/sbin/httpd: Mach-O universal binary with 4 architectures
/usr/sbin/httpd (for architecture ppc7400): Mach-O executable ppc
/usr/sbin/httpd (for architecture ppc64): Mach-O 64-bit executable ppc64
/usr/sbin/httpd (for architecture i386): Mach-O executable i386
/usr/sbin/httpd (for architecture x86_64): Mach-O 64-bit executable x86_64

Oooh, I see 64-bit stuff in there, don’t I? I suppose that could be a problem if it’s trying to load a 32-bit library. Well, the packaged PHP that I installed from entropy.ch hasn’t been updated yet for Leopard, so I guess I’ll have to download and compile PHP myself so I can get in on the 64-bit goodness.

*insert footage of Brian downloading a bunch of stuff, typing ./configure and make, and seeing copious error messages fly across his terminal window*

Hmmm, OK, I think someone smarter than me is going to need to figure this stuff out. But I don’t have time for that, I want to get my FogBugz up and running so I don’t have to switch back to Tiger just to answer customer e-mail. If I could just force Apache to run as 32-bit instead of 64-bit, it should be able to load this PHP module just fine. Now let’s see, what would Dr. 90210 do…?

[bw-mbp:~] bwebster% man lipo

Yes, this is extremely hacktackular, and I’m sure there must be a better way to do this, but this is what I got.

[bw-mbp:~] bwebster% cd /usr/sbin

[bw-mbp:~] bwebster% sudo cp httpd httpd-fat

[bw-mbp:~] bwebster% sudo lipo httpd -thin i386 -output httpd

After making a backup of the httpd executable for safety purposes, the lipo commands sucks out all of the architectures included in the universal binary except the 32-bit Intel one (specified by “i386″). Apache can’t run as 64-bit if it doesn’t have a 64-bit binary!

After doing this, starting up the web server again worked just fine, and loaded the existing 32-bit PHP5 module. Once I got all my httpd.conf customizations moved over to the new location, that was pretty much all I needed to do. It also turns out that I probably would have run into this same problem for FogBugz specifically, since it loads its own fogutil.so PHP module which is also only available as 32-bit right now. I don’t know for sure, but I’m guessing people running on G5s, which also support 64-bit, would probably need the same trick, except replacing “ppc” for “i386″.

This is obviously a very poor long term solution, but if anyone needs to get FogBugz up and running on Leopard right away, this did the trick for me. Ultimately, getting FogBugz to work hack-free will require someone to get a 64-bit build of the PHP5 Apache module figured out, as well as Fog Creek providing a 64-bit build of their own Apache module. And of course what I’ve outlined here is totally unsupported by Fog Creek, so proceed at your own risk!

iPhoto Library Manager and Leopard

Posted on October 12, 2007 by Brian Webster
Filed Under Development, iPhoto Library Manager | 1 Comment

I just thought I would drop a quick note regarding plans for Leopard compatibility for iPhoto Library Manager. The short version is that iPhoto Library Manager will support Leopard fully by the time Leopard comes out. Yay! Since things can change and break things at the last minute with new OS releases, I won’t actually be releasing a Leopard compatible version of iPLM until I can actually test it with the final version of Leopard, but I’ve got the big stuff figured out already. For those interested in the nerdy technical details, read onward.

Leopard will require some changes in the way iPhoto Library Manager works under the hood. iPLM uses the Input Manager mechanism of Mac OS X to load a bundle of code into iPhoto which supplements iPhoto’s Applescripting capabilities, which is what makes features such as copying albums and merging libraries possible. Some other programs, such as Inquisitor and 1Passwd also use input managers to implement their functionality. However, as has been reported by Ars Technica and discussed on some other blogs, input managers are no longer going to be supported under Leopard.

Other developers have found other methods to do what they need to do under Leopard, such as 1Passwd, whose developers say they are going to switch to a WebKit plugin, which works well for them since their product is web browser oriented. iPhoto Library Manager is going to take a different approach though.

One difference between iPLM and some other apps that use input managers is that iPLM only actually needs its code to be loaded when it’s actively doing something with iPhoto, like copying some albums between libraries. It doesn’t matter if the code is loaded every time iPhoto is launched, unlike say, Inquisitor, which pretty much always has to be there to be of any use.

So, instead of a plugin based approach, under Leopard iPLM will use a handy little feature of the OS X dynamic linker, the DYLD_INSERT_LIBRARIES environment variable. Basically what this does is allow you to load additional dynamic libraries in an application (and even substitute for libraries the application is already linked to). The most well known usage for this feature is the MallocDebug developer application, which loads a custom debug version of the malloc library that replaces the normal system library and provides all sort of information on memory usage in the application being debugged.

The downside to this approach is that it requires relaunching the application in question in order to load the additional code, since you have to set up this environment variable and then launch the application yourself. So, for something like Inquisitor, launching Safari directly from the dock would not load the code automatically. There would have to be a separate program that performs the special setup and launch Safari itself, which would be a pain in the butt for users. However, this isn’t really a problem for iPLM, since it has to relaunch iPhoto multiple times during an album copy anyway, so having to do the special setup doesn’t really change the flow of things at all.

There are a couple other upsides to this approach:

The only feature that will be lost as a result of the change is putting the name of the current library in the title bar of the iPhoto window itself. This was a neat addition that I put in on a whim, but since the iPLM code will no longer always be running in iPhoto, this will be going away.

In retrospect, if I had known about this method of doing things back when I first wrote iPLM 3.0, I probably would have done it this way in the first place, since it’s less intrusive and uses a long standing feature of dyld that isn’t likely to go anywhere anytime soon. Like I said above, this method isn’t feasible for many programs that are currently using input managers, but this may still prove a useful technique to some other programs transitioning over to Leopard.

Subclassing NSSortDescriptor for fun and profit

Posted on September 7, 2007 by Brian Webster
Filed Under Cocoa, Development, Programming | 1 Comment

In the project I’m currently working on, I’m using a pretty standard NSTableView + NSArrayController setup using the default sorting functionality. However, I found myself wanting to do some custom sorting, i.e. not just the usual sorting provided by @selector(compare:). This is made a little more complex by the fact that the columns of the table can be dynamically added and removed, so it’s not quite as simple as just changing the sort selector in Interface Builder. On top of that, I later found that I needed more customization still, because I needed to do special handling of nil values that was different from NSSortDescriptor’s behavior.

So, I set off to make a custom subclass of NSSortDescriptor. I can just override compareObject:toObject:, easy as pie! Right? Er… not quite. There really isn’t any information at all in the Cocoa docs about subclassing NSSortDescriptor, and there are a few gotchas that aren’t necessarily obvious when implementing the subclass, especially if you’re using it with an NSTableView.

To begin, I made my subclass (ITSortDescriptor) and overrode the compareObject:toObject: method. This worked fine to begin with, but I discovered that if I clicked on a table column to change the sort order of the table, it would soon revert to the default sort behavior. After some debugging, I discovered that the reason for this was that NSTableView was making copies of the columns’ sort descriptors using the NSCopying protocol. NSSortDescriptor implements NSCopying, but apparently its implementation will always return an instance of NSSortDescriptor, even the object being copied is an NSSortDescriptor subclass. So, I implemented copyWithZone: in ITSortDescriptor to return an instance of the subclass rather than a plain NSSortDescriptor. The implementation is pretty straightforward:

- (id)copyWithZone:(NSZone*)zone
{
return [[ITSortDescriptor alloc] initWithKey:[self key] ascending:[self ascending] selector:[self selector]];
}

This solved the problem for some cases, but there were still times when the column would revert to its old sorting behavior. I narrowed it down to when the table was already sorted by the column in question, and then you clicked on that column again to reverse the sort order. Apparently, NSSortDescriptor’s implementation of reversedSortDescriptor suffers from the same problem as its implementation of copyWithZone:. Overriding that in a similar manner then fixed that problem:

- (id)reversedSortDescriptor
{
return [[[ITSortDescriptor alloc] initWithKey:[self key] ascending:![self ascending] selector:[self selector]] autorelease];
}

To be really cool, instead of having [ITSortDescriptor alloc], that could instead be [[self class] alloc], but that would only really be useful if that’s what NSSortDescriptor did itself.

So with those modifications, I now have my table sorting just the way I want it. I thought I’d blog this so that anyone else who came across this stuff and was banging their heads against the wall might find it and save themselves some pain.

keep looking »

Feed