Software that doesn’t work with Mac OS X Lion

Since upgrading to Mac OS X Lion I’ve found the following software on my System that no longer works.

Games & apps that require Rosetta (PowerPC compatibility)

  • Age of Empires 2 Gold
  • Baldur’s Gate 2
  • Diablo 2
  • DooM Legacy
  • Railroad Tycoon 3
  • Starcraft

Games and apps that crash on start

  • The Dig
  • Indiana Jones and the Last Crusade
  • Guitar Rig 4 64bit (patch expected soon)
Advertisements

Even more Cocoaheads Presentations

I’ve continued my journey with Final Cut Pro X and created more videos from talks done at the June Melbourne Cocoaheads meetup. These videos are now up on the Melbourne Cocoaheads Vimeo group (and embedded below).

David Kennedy & Scott Manley of Dangerous Pixels speak about building their iOS app development consultancy and their application Task Caddy.

You can find a wrap-up from the Dangerous Pixel guys, with links, slides and other resources over on their blog.

Luke Cunningham & Jesse Collis on “Epic Refactorings and Patterns to Make Your Code Awesome”.

Cancellable asynchronous searching with UISearchDisplayController

Apple’s UISearchDisplayController is a handy wrapper around searching for UITableViewControllers. UISearchDisplayController will send its delegate the following message whenever a user types something into the search bar:

 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller 
shouldReloadTableForSearchString:(NSString *)searchString

It is in this method that the delegate should perform a search of its data and based on whether the search results have or would change return the value YES or NO.

This all works fine and dandy when the objects to search are in memory and the delegate method executs promptly. If this is not the case then the UI will block while the search logic is performed. This can create significant lag in the user interface as the user is typing into the search field.

In the iOS API documentation for searchDisplayController:shouldReloadTableForSearchString: Apple provides a hint about performing search work in the background but doesn’t really go into any detail. The docs say:

You might implement this method if you want to perform an asynchronous search. You would initiate the search in this method, then return NO. You would reload the table when you have results.

An approach to doing an asynchronous search might be something like this:

@interface MyUISearchDisplayDelegate ()
@property (nonatomic, retain) NSOperationQueue *searchQueue;
@property (nonatomic, retain) NSArray *searchResults;
@end

@implementation MyUISearchDisplayDelegate
@synthesize searchQueue, searchResults;

- (id)init
{
    if ((self = [super init]))
    {
        self.searchResults = [NSMutableArray array];
        self.searchQueue = [[NSOperationQueue new] autorelease];
        [self.searchQueue setMaxConcurrentOperationCount:1];
    }
    
    return self;
}

- (void)dealloc
{
    self.searchResults = nil;
    self.searchQueue = nil;
    
    [super dealloc];
}

// ...

 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
   if  ([searchString isEqualToString:@""] == NO)
    {
        [self.searchQueue addOperationWithBlock:^{
            
            NSArray *results = // fetch the results from 
                // somewhere (that can take a while to do)
            
            // Ensure you're assigning to a local variable here.
            // Do not assign to a member variable.  You will get
            // occasional thread race condition related crashes 
            // if you do.            

            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                // Modify your instance variables here on the main
                // UI thread.
                [self.searchResults removeAllObjects]; 
                [self.searchResults addObjectsFromArray:results];
                
                // Reload your search results table data.
                [controller.searchResultsTableView reloadData];
            }];
        }];
        
        return NO;
    }
    else
    {
        [self.searchResults removeAllObjects];
        return YES;
    }
}

This implementation gets you some of the way there. The problem with it however is that as the user interacts with the UISearchBar the searchString changes and with each character typed another invocation of searchDisplayController:shouldReloadTableForSearchString: is performed. This causes lots of work to be enqueued into the NSOperationQueue. Work that must get executed in sequence.

This is not ideal. The user is only interested in the most recent search string that they have entered. If they enter the string “fubar” into the UISearchBar they don’t care about the searches for f, fu, fub, and fuba.

OK. So how do you properly implement this? Well, it is surprisingly easy. Just cancel all the existing operations in the NSOperationQueue before adding your latest search operation to the queue.

     [self.searchQueue cancelAllOperations];

Any yet to execute operations will be cancelled and never execute. Any presently executing operation will run to completion. In a more complex situation, perhaps with a multistep NSOperation, (where you were using a custom subclass of NSOperation or something) you could check for isCanceled on NSOperaton instance and bail out of your operation early. I’ll leave that up to you to figure out.

So, with this change the code becomes:

 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller 
shouldReloadTableForSearchString:(NSString *)searchString
{
   if  ([searchString isEqualToString:@""] == NO)
    {
        [self.searchQueue cancelAllOperations];
        [self.searchQueue addOperationWithBlock:^{
            
            NSArray *results = // fetch the results from
              // somewhere (that can take a while to do)
            
            // Ensure you're assigning to a local variable here.
            // Do not assign to a member variable.  You will get
            // occasional thread race condition related crashes 
            // if you do.            

            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                // Modify your instance variables here on the main
                // UI thread.
                [self.searchResults removeAllObjects]; 
                [self.searchResults addObjectsFromArray:results];
                
                // Reload your search results table data.
                [controller.searchResultsTableView reloadData];
            }];
        }];
        
        return NO;
    }
    else
    {
        [self.searchResults removeAllObjects];
        return YES;
    }
}

One final little thing, it is probably a good idea to cancel any pending operations when the user dismisses the search UI.

- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    [self.searchQueue cancelAllOperations];
}

You may wish to cancel any pending operations in other delegate methods too. Such as tableView:didSelectRowAtIndexPath: for example.

Using p4merge as a custom Git merge tool on Mac OS X.

On Mac OS X Git uses the opendiff(1) command line utility as its merge tool by default.

opendiff(1) will launch the FileMerge app that comes with XCode when performing merges. Unfortunately FileMerge is not a very good merge tool.

To improve this, there are a number of options. There are some good commercial, Open Source and freeware diff tools out there for Mac OS X such as DiffMerge, SureMerge, Araxis Merge, K3Diff, and p4merge, etc. Too many to list them all here.

For my system I chose p4merge. p4merge is part of the Perforce source control management system. I’ve used Perforce quite a lot in the past at previous jobs and it has great tools. Perforce is commercial software, and I’m not about to use Perforce as my source control system instead of Git, but fortunately you can download and use the Perforce GUI tools for free. The Perforce merge tool is not tied in any way to the Perforce SCM system so you can use it as a stand alone tool.

To set up p4merge as my mergetool with Git I did the following:

  1. Downloaded the Perforce Visual Client disk image from Perforce.com
  2. Installed p4merge into /Applications.
  3. Added the these settings to my ~/.gitconfig file.

Now git mergetool will launch p4merge as my preferred merge tool.

p4merge Screenshot

Now you may be wondering why I didn’t just set my Git merge.tool config setting to p4merge, as it is supported “by default” by Git. Well, Git expects the command p4merge to be in the $PATH and I’d rather not have to install shell scripts across the different Mac systems I use so that it works “out of the box”. I also found p4merge wasn’t dealing well with the relative paths that Git was trying to pass to it, hence the custom mergetool.custom.cmd setting that uses $PWD.

Use the right cable.

Lately I had become somewhat dissatisfied with the speed of my QNAP 509 Pro NAS. In particular with the speed of transfers to and from it. I was thinking about getting another one, or replacing the drives in and effort to speed it up.

But then I gave it some thought and figured I should probably first do some more testing and research.

QNAP advertise the NAS I have as being able to get a sustained 60 megabytes per second in file transfers over a single gigabit ethernet link. This is pretty respectable. However, I was only getting at most 12MB/s. At first I thought that my iMac not supporting “jumbo frames” could be the problem. But with a little more digging I discovered the QNAP doesn’t support jumbo frames either so that wasn’t going to help me anyway.

In an effort to rule out the iMac as the problem I hooked up my MacBook Pro, which is normally only connected via Wi-Fi, to the Apple Time Capsule that is serving as my gigabit ethernet switch and did some more testing. The MacBook Pro was having similarly bad performance transferring to and from the QNAP. In an effort to discover if data transfer performance was just generally bad or if it was just the QNAP that was suffering bad performance I tried transferring files between my MacBook Pro and my iMac. This is where things started to get interesting. My MacBook was happily transferring data to and from my iMac at around 60MB/s.

It was at this point that a silly thought popped into my mind. Perhaps the cable connecting the Time Capsule to the QNAP just wasn’t up to snuff when it came to gigabit ethernet. So I changed it, and things got better. Fast.

Afterwards I realised that the cable I had been using all this time, to connect my QNAP to the rest of the world, the cable that I had just randomly selected from the large collection I have, was a cable I made myself a long time ago when CAT5 and 100MBps networks were the norm. The cable I replaced it with was a professionally made, modern, CAT5E cable. One designed to work with gigabit ethernet.

What a difference using the right cable can make.

Foggy CarrierWave Nokogiri woes on Mac OS X Snow Leopard

I’m developing a Ruby on Rails app on my Mac running Snow Leopard. Tonight I wanted to use the fog gem with CarrierWave to upload files to Amazon S3.

Fog is a Ruby wrapper around different cloud services and CarrierWave provides a very nice API for handling file uploads in Rails apps.

Fog has a dependency on the Nokogiri HTML/XML parsing library, which has a dependency on libxml2, libxslt, and libiconv.

Welcome to pain and suffering…

The versions of libxml2 and libxslt on MacOS X are a little out of date. And when trying to install the nokogiri gem with:

sudo gem install nokogiri

You will get an error like this:


ERROR: Error installing nokogiri:
ERROR: Failed to build gem native extension.
...
checking for libxml/parser.h... yes
checking for libxslt/xslt.h... yes
checking for libexslt/exslt.h... yes
checking for iconv_open() in iconv.h... no
checking for iconv_open() in -liconv... no
-----
libiconv is missing.
please visit http://nokogiri.org/tutorials/installing_nokogiri.html
for help with installing dependencies.
-----

Now, libiconv isn’t missing. It is installed with MacOS X. But nokogiri’s build script can’t find it for some reason.

The Nokogiri install instructions suggest installing newer versions of libxml2 and libxslt to solve this problem. They even kindly provide instructions for a number of operating systems including MacOS X.

Unfortunately none of them worked for me.

I’m using homebrew to manage my additional source packages on my Mac. Installing libxml2 via homebrew is easy:

brew install libxml2

Installing libxslt, not so much.

There is no brew formula for libxslt in the default homebrew git repo. I have no idea why. Perhaps something to do with homebrew’s policy not to dupe stuff bundled with MacOS X. Not sure why they provide a libxml2 forumla then as it comes with MacOS X too.

So to install libxslt I did the following:


curl ftp://xmlsoft.org/libxml2/libxslt-1.1.26.tar.gz -o libxslt-1.1.26.tar.gz
tar zxfv libxslt-1.1.26.tar.gz
cd libxslt-1.1.26
./configure \
--prefix=/usr/local/Cellar/libxslt/1.1.26 \
--with-libxml-prefix=/usr/local/Cellar/libxml2/2.7.7
make
make install
cd .. && rm -rf libxslt-1.1.26 && rm -f libxslt-1.1.26.tar.gz

To install nokogiri I then used the following command line.


sudo gem install nokogiri -- \
--with-xslt-dir=/usr/local/Cellar/libxslt/1.1.26 \
--with-xml2-include=/usr/local/Cellar/libxml2/2.7.7/include/libxml2 \
--with-xml2-lib=/usr/local/Cellar/libxml2/2.7.7/lib \
--with-iconv-dir=/usr

Now everything seems to be working ok.