<![CDATA[Forge Code]]> 2011-11-22T00:34:09+11:00 http://forgecode.net/ Octopress <![CDATA[Compile Time Checking of KVC Keys]]> 2011-11-21T16:32:00+11:00 http://forgecode.net/2011/11/compile-time-checking-of-kvc-keys KVC (Key Value Coding) is a pretty great technology. It’s used throughout the Cocoa APIs, and you might not even realise you’re using it. One of the more common tasks which makes use of KVC is sorting with NSSortDescriptor objects. Here’s a quick example snippet:

@interface Person : NSObject
@property () NSUInteger age;
@end

...

NSArray *people;  // Contains Person objects
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]
NSArray *peopleSortedByAge = [people sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

One of the great aspects to using a sort descriptor with KVC like this is that you don’t have to worry about the type that -age will return. It could be an int, an NSString or an NSNumber, and KVC and NSSortDescriptor will take care of it for you.

The Problem: String Literals

There’s one really big downside to KVC though, which is that you need to use string literals for the ‘Key’ part of ‘Key Value Coding’. This makes your code fragile in a number of ways.

  • You need to specify them in multiple places
  • Renaming the property or method means you have to update multiple strings
  • Typos won’t be discovered until run-time, when you’ll get an exception

One approach to try and reduce these problems is to create a constant string for every key you want to use with KVC. This solves the first issue, reduces the second one (you still have to remember to update the constant key string when re-factoring), but doesn’t solve the third.

A little while back, Uli Kusterer published a blog post describing a macro that, when used with the -Wundeclared-selector compiler flag, would throw a compiler warning for keys that don’t exist as a method.

#define PROPERTY(propName)    NSStringFromSelector(@selector(propName))


// usage:
NSSortDescriptor *ageDescriptor = [NSSortDescriptor sortDescriptorWithKey:PROPERTY(age) ascending:YES];

This solves all of the problems with string literals above, but has one other problem; you will only get a warning if the key does not match up with a selector on any class. This means that the following will compile without warning, but throw an exception at run-time:

@interface Polygon : NSObject

@property (strong) NSArray *lengths;

@end


@interface Line : NSObject

@property () CGFloat length;

@end

...

NSArray *lines;   // Contains Line objects
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:PROPERTY(lengths) ascending:YES];
NSArray *sortedLines = [lines sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

The Key() Macro

In order to be confident that you’re not going to get an exception at run-time, you need to check that instances of a particular class respond to the selector, not just check that the selector has been declared. Recently I decided to dedicate a few hours to trying to come up with a macro that you can use like this:

sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:Key(Line, length)];

The first breakthrough in trying to create such a macro came when I realised that you could get a compile-time check of whether or not a class responds to a selector, while not actually causing anything to happen at run-time, by using a typecast nil:

[(Line *)nil length];   // Compiles Fine
[(Line *)nil lengths];  // Compiler Warning

You can wrap this up in a macro like this:

#define Key(class, key)      [(class *)nil key]

But the macro needs to not only check that the class responds to the key, it needs to return the key as an NSString. As far as I knew at the time, it was impossible to write a C macro that executed one expression, and returned the value of a second expression. After a few hours of research and experimentation, I discovered a neat trick: you can use C’s ternary operator to do exactly this, like so:

#define MyMacro(arg)     (expr1(arg) ? expr(arg) : expr2(arg))

This macro will run expr1(arg), and return the value of expr2(arg). This trick can be combined with the typecast nil trick, to give use a fully functional Key() macro!

The Actual Macro

#define Key(class, key)      ([(class *)nil key] ? @#key : @#key)

#define ProtocolKey(protocol, key)   ([(id <protocol>)nil key] ? @#key : @#key)

I’ve also shown a variant that allows you to get a KVC key for a method declared on a protocol, not on a class itself.

In theory, using these macros is slightly slower than just using a string literal (or constant string), but the overhead is so tiny (messaging nil, then evaluating a single ternary branch) that it should be negligible. With compiler optimisation turned on, the ternary branch should (I hope) be optimised out, and the messaging nil part may also be optimised out (although again, the performance hit should be negligible if it isn’t). Despite that, I have used an ifdef to shortcut the macro in Release builds:

#ifdef DEBUG
#define Key(class, key)              ([(class *)nil key] ? @#key : @#key)
#define ProtocolKey(protocol, key)   ([(id <protocol>)nil key] ? @#key : @#key)
#else
#define Key(class, key)              @#key
#define ProtocolKey(class, key)      @#key
#endif

Now you can safely use KVC with compile-time checking. If you aren’t using ARC, you will need to enable the -Wundeclared-selector compiler warning (it shows up in ‘Build Settings’ as ‘Undeclared Selector’ in recent versions of Xcode 4). Under ARC, you don’t need to turn this warning on for these macros to work - undeclared selectors will cause build errors no matter what (which is probably better for this particular situation). Hopefully you find these macros as useful as I do!

]]>
<![CDATA[Migrating to Octopress]]> 2011-11-20T18:25:00+11:00 http://forgecode.net/2011/11/migrating-to-octopress After putting it off for over 2 years, I’ve migrated my blog from WordPress to something statically generated. Like many developers working with iOS and Mac OS X, I’ve decided to jump on the Octopress bandwagon.

After a few minor teething issues getting RVM configured on Mac OS X 10.7, and quite a few problems getting comments migrated from WordPress to Disqus (third time’s the charm!), I finally got the migration completed this weekend. Now that I don’t have to deal with WordPress (which was giving me the same feeling of unease that I get when I have to use Windows) hopefully you’ll start seeing a few more posts from me!

]]>
<![CDATA[The Modern Runtime and Clang 1.6's Shadowed Variables Bug]]> 2010-11-28T00:00:00+11:00 http://forgecode.net/2010/11/the-modern-runtime-and-clang-1-6s-shadowed-variables-bug If you’re writing code in Objective-C, and haven’t heard about the ‘@synthesize by Default’ (and to a lesser extent, ‘Ivars in Class Extensions’), you owe it to yourself to go and read up about it. Martin Pilkington describes the advantages of these new compiler/runtime features in his post New Objective-C Features. Personally, I’ve found ‘@synthesize by Default’ to be a fantastic aid to my productivity, as it means you can create or modify Obj-C properties in only one place (instead of having to keep your @property and @synthesize statements in sync).

Enabling the ‘Modern Runtime’

To enable these features, you need to be using the “Modern Runtime”, with the LLVM compiler. You should be using the LLVM compiler anyway (unless you have a good reason not to), since it’s usually faster, and it gives you much more helpful warning and error messages than GCC. In recent versions of Xcode, LLVM even shows you the exact character (not just the line) in your code where the warning or error was found.

Assuming you’re using LLVM, to enable the “Modern Runtime” and get these great new compiler/runtime features, you need to add the following to the “Other C Flags” Build Setting in Xcode:

-Xclang -fobjc-nonfragile-abi2

Now you just need to build, and everything should work…

Clang 1.6 Chokes on Shadowed Variables

If you’re like me, when you added the above Build Setting then hit “Build” in Xcode, you got an error that looked something like this:

clang: error: clang frontend command failed due to signal 11 (use -v to see invocation) Command /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang failed with exit code 245

After trying the usual places (Google, StackOverflow etc.), I went to the Apple Developer Forums. There was one thread where people described this same problem, and another one where a user proposed a cause (and hence, a solution). It turns out that Clang 1.6 (the version that ships with iOS SDK 4.2 and/or Xcode 3.2.5) has some pretty severe bugs when it comes to the Modern Runtime and shadowed variables. A shadowed variable is one that has been re-declared in a more localised scope. This code snippet shows a common example of a shadowed variable:

@property (retain) UIWebView *webView;



@synthesize webView;



- (void)webViewDidFinishLoad:(UIWebView *)webView
{
  NSLog(@"Web View Loaded!");
}

In the -webViewDidFinishLoad: method, webView refers to the method argument, even though webView was already implicitly declared as the auto-synthesized ivar that is backing the .webView property. This is known as a ‘shadowed variable’, and is relatively common in Objective-C in method parameter names. Note that if you had manually declared an ivar named webView, it would also be a shadowed variable. Unfortunately, when Clang 1.6 with the ‘modern runtime’ turned on encounters this code, it will often crash.

To work around this, you will need to get rid of the offending shadowed variables in your code. I’ve found that shadowed variables due to method parameters often cause the compiler to crash, which means you have to go through the file that caused the clang: error: clang frontend command failed due to signal 11 error and find it yourself (if the compiler actually crashes, you often won’t get a warning or error with a line number). Other shadowed variables can be found by turning on the “Hidden Local Variables” compiler warning Build Setting in Xcode. On another project, turning “Hidden Local Variables” on made Clang go into infinite recursion instead of crashing, so I got tens of thousands of warnings for each shadowed variable, which made Xcode hang for 3-4 minutes. To get around this, you can temporarily (or permanently - Peter Hosey calls it ’hardass mode’) turn on the “Treat Warnings as Errors” compiler warnings Build Setting. This will make the compiler stop before it spins out of control in infinite recursion, which should allow you to fix each shadowed variable before building again and moving onto the next one.

Once you’ve gotten rid of all the shadowed variables in your code, you should be free to make use of the language and runtime features of the Modern Runtime, like declaring your properties in a single line of code. Enjoy!

]]>
<![CDATA[UIFont Name Grabber]]> 2010-08-06T00:00:00+10:00 http://forgecode.net/2010/08/uifont-name-grabber It’s been a long time between posts here on Forge Code. Since my last post, I’ve made the transition to become a full time freelance iPhone/iPad developer, which, alongside various personal and family events, has meant that my time has been at a premium. This doesn’t mean that I don’t have anything to write about - in fact, quite the opposite. I now have a bunch of topics and snippets queued up in the back of my brain, it’s just a matter of finding the time to get them out into the aether.

Now, onto the actual post…

Creating UIFont objects

The most common method of creating or representing fonts in code in iOS is to use +[UIFont fontWithName:size:]. It’s very straightforward to use: to get a UIFont instance that represents Helvetica 12 point, you would do this:

UIFont *font = [UIFont fontWithName:@"Helvetica" size:12.0];

This is simple, but what’s not so simple is knowing what string you would use to select font variations like Helvetica Bold or Helvetica Oblique. The string argument passed to fontWithName:size: needs to be one of a pre-defined list of available system fonts (or, on iOS 3.2 and later, an imported font). UIFont allows you to get the list of available ‘family names’ (e.g. Helvetica), and then the list of ‘font names’ that belong to a particular ‘family’ (e.g. Helvetica, Helvetica-Bold, Helvetica-BoldOblique and Helvetica-Oblique) by using the +familyNames and +fontNamesForFamilyName: class methods. The strings returned by +fontNamesForFamilyName: are the values that can be used as an argument for +fontWithName:size:. Writing code to iterate through these lists is easy enough, but it’s tedious having to do it every time you want to access to what should really be part of the Cocoa Touch API reference documentation.

UIFont Name Grabber

To get around this problem, I’ve written a very simple universal iOS app that grabs these lists of fonts, serialises them into both plain text and XML plist formats, then attaches them to an email. Email seemed like the most straightforward way of getting files from any iOS device to a development Mac. The attachments have the OS version number in the filename, so it’s easy to gather these files from different OS versions and compare the available fonts. I’ve tested the app on iPhone OS 3.1.3, 3.2.1 and 4.0.1.

Here’s the code that grabs (and formats) the lists of fonts:

NSMutableDictionary *fontsByFamilyName = [NSMutableDictionary dictionary];
NSMutableString *fontsString = [NSMutableString string];

NSArray *sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"self" ascending:YES] autorelease]];

NSArray *familyNames = [[UIFont familyNames] sortedArrayUsingDescriptors:sortDescriptors];
for (NSString *aFamilyName in familyNames) {
  NSArray *fontNames = [[UIFont fontNamesForFamilyName:aFamilyName] sortedArrayUsingDescriptors:sortDescriptors];

  [fontsByFamilyName setObject:fontNames forKey:aFamilyName];

  [fontsString appendFormat:@"%@\n", aFamilyName];
  for (NSString *aFontName in fontNames) {
      [fontsString appendFormat:@"\t%@\n", aFontName];
  }
  [fontsString appendString:@"\n"];
}

// Create an XML plist data representation of the dictionary
NSData *fontsPlistAsData = [NSPropertyListSerialization dataFromPropertyList:fontsByFamilyName
                                                                    format:NSPropertyListXMLFormat_v1_0
                                                          errorDescription:nil];

// Create a UTF8 data representation of the string
NSData *fontsTxtAsData = [fontsString dataUsingEncoding:NSUTF8StringEncoding];

The app is freely available on github here: UIFont-Name-Grabber. Hopefully others will find it as useful as I do.

]]>
<![CDATA[Built-in System Fonts on iOS 4.0.1, 3.2.1 and 3.1.3]]> 2010-08-06T00:00:00+10:00 http://forgecode.net/2010/08/built-in-system-fonts-on-ios-4-0-1-3-2-1-and-3-1-3 As a follow up to my last post about UIFont Name Grabber, I’m uploading a list of the fonts available on iOS 4.0.1, 3.2.1 and 3.1.3. At the time of writing these are the most recent iPhone OS 3 for iPhone, iPhone OS 3 for iPad and iOS4 for iPhone releases publicly available, and are therefore the OS versions you will most likely be targeting during development.

If you want these lists as a txt file or XML plist, you can grab the UIFont Name Grabber app from github and generate them yourself, or grab them from here:

UIFont Names for iPhone OS 4.0.1.zip

UIFont Names for iPhone OS 3.2.1.zip

UIFont Names for iPhone OS 3.1.3.zip

iPhone OS 4.0.1

American Typewriter
    AmericanTypewriter
    AmericanTypewriter-Bold

AppleGothic
    AppleGothic

Arial
    Arial-BoldItalicMT
    Arial-BoldMT
    Arial-ItalicMT
    ArialMT

Arial Hebrew
    ArialHebrew
    ArialHebrew-Bold

Arial Rounded MT Bold
    ArialRoundedMTBold

Bangla Sangam MN
    BanglaSangamMN
    BanglaSangamMN-Bold

Baskerville
    Baskerville
    Baskerville-Bold
    Baskerville-BoldItalic
    Baskerville-Italic

Cochin
    Cochin
    Cochin-Bold
    Cochin-BoldItalic
    Cochin-Italic

Courier
    Courier
    Courier-Bold
    Courier-BoldOblique
    Courier-Oblique

Courier New
    CourierNewPS-BoldItalicMT
    CourierNewPS-BoldMT
    CourierNewPS-ItalicMT
    CourierNewPSMT

DB LCD Temp
    DBLCDTempBlack

Devanagari Sangam MN
    DevanagariSangamMN
    DevanagariSangamMN-Bold

Futura
    Futura-CondensedExtraBold
    Futura-Medium
    Futura-MediumItalic

Geeza Pro
    GeezaPro
    GeezaPro-Bold

Georgia
    Georgia
    Georgia-Bold
    Georgia-BoldItalic
    Georgia-Italic

Gujarati Sangam MN
    GujaratiSangamMN
    GujaratiSangamMN-Bold

Gurmukhi MN
    GurmukhiMN
    GurmukhiMN-Bold

Heiti J
    STHeitiJ-Light
    STHeitiJ-Medium

Heiti K
    STHeitiK-Light
    STHeitiK-Medium

Heiti SC
    STHeitiSC-Light
    STHeitiSC-Medium

Heiti TC
    STHeitiTC-Light
    STHeitiTC-Medium

Helvetica
    Helvetica
    Helvetica-Bold
    Helvetica-BoldOblique
    Helvetica-Oblique

Helvetica Neue
    HelveticaNeue
    HelveticaNeue-Bold

Hiragino Kaku Gothic ProN
    HiraKakuProN-W3
    HiraKakuProN-W6

Kannada Sangam MN
    KannadaSangamMN
    KannadaSangamMN-Bold

Malayalam Sangam MN
    MalayalamSangamMN
    MalayalamSangamMN-Bold

Marker Felt
    MarkerFelt-Thin
    MarkerFelt-Wide

Oriya Sangam MN
    OriyaSangamMN
    OriyaSangamMN-Bold

Palatino
    Palatino-Bold
    Palatino-BoldItalic
    Palatino-Italic
    Palatino-Roman

Sinhala Sangam MN
    SinhalaSangamMN
    SinhalaSangamMN-Bold

Tamil Sangam MN
    TamilSangamMN
    TamilSangamMN-Bold

Telugu Sangam MN
    TeluguSangamMN
    TeluguSangamMN-Bold

Thonburi
    Thonburi
    Thonburi-Bold

Times New Roman
    TimesNewRomanPS-BoldItalicMT
    TimesNewRomanPS-BoldMT
    TimesNewRomanPS-ItalicMT
    TimesNewRomanPSMT

Trebuchet MS
    Trebuchet-BoldItalic
    TrebuchetMS
    TrebuchetMS-Bold
    TrebuchetMS-Italic

Verdana
    Verdana
    Verdana-Bold
    Verdana-BoldItalic
    Verdana-Italic

Zapfino
    Zapfino

iPhone OS 3.2.1

Academy Engraved LET
    AcademyEngravedLetPlain

American Typewriter
    AmericanTypewriter
    AmericanTypewriter-Bold

AppleGothic
    AppleGothic

Arial
    Arial-BoldItalicMT
    Arial-BoldMT
    Arial-ItalicMT
    ArialMT

Arial Hebrew
    ArialHebrew
    ArialHebrew-Bold

Arial Rounded MT Bold
    ArialRoundedMTBold

Baskerville
    Baskerville
    Baskerville-Bold
    Baskerville-BoldItalic
    Baskerville-Italic

Bodoni 72
    BodoniSvtyTwoITCTT-Bold
    BodoniSvtyTwoITCTT-Book
    BodoniSvtyTwoITCTT-BookIta

Bodoni 72 Oldstyle
    BodoniSvtyTwoOSITCTT-Bold
    BodoniSvtyTwoOSITCTT-Book
    BodoniSvtyTwoOSITCTT-BookIt

Bodoni 72 Smallcaps
    BodoniSvtyTwoSCITCTT-Book

Bodoni Ornaments
    BodoniOrnamentsITCTT

Bradley Hand
    BradleyHandITCTT-Bold

Chalkduster
    Chalkduster

Cochin
    Cochin
    Cochin-Bold
    Cochin-BoldItalic
    Cochin-Italic

Copperplate
    Copperplate
    Copperplate-Bold

Courier
    Courier
    Courier-Bold
    Courier-BoldOblique
    Courier-Oblique

Courier New
    CourierNewPS-BoldItalicMT
    CourierNewPS-BoldMT
    CourierNewPS-ItalicMT
    CourierNewPSMT

DB LCD Temp
    DBLCDTempBlack

Didot
    Didot
    Didot-Bold
    Didot-Italic

Futura
    Futura-CondensedExtraBold
    Futura-Medium
    Futura-MediumItalic

Geeza Pro
    GeezaPro
    GeezaPro-Bold

Georgia
    Georgia
    Georgia-Bold
    Georgia-BoldItalic
    Georgia-Italic

Gill Sans
    GillSans
    GillSans-Bold
    GillSans-BoldItalic
    GillSans-Italic

Heiti J
    STHeitiJ-Light
    STHeitiJ-Medium

Heiti K
    STHeitiK-Light
    STHeitiK-Medium

Heiti SC
    STHeitiSC-Light
    STHeitiSC-Medium

Heiti TC
    STHeitiTC-Light
    STHeitiTC-Medium

Helvetica
    Helvetica
    Helvetica-Bold
    Helvetica-BoldOblique
    Helvetica-Oblique

Helvetica Neue
    HelveticaNeue
    HelveticaNeue-Bold
    HelveticaNeue-BoldItalic
    HelveticaNeue-Italic

Hiragino Kaku Gothic ProN
    HiraKakuProN-W3
    HiraKakuProN-W6

Hiragino Mincho ProN
    HiraMinProN-W3
    HiraMinProN-W6

Hoefler Text
    HoeflerText-Black
    HoeflerText-BlackItalic
    HoeflerText-Italic
    HoeflerText-Regular

Marker Felt
    MarkerFelt-Thin
    MarkerFelt-Wide

Optima
    Optima-Bold
    Optima-BoldItalic
    Optima-Italic
    Optima-Regular

Palatino
    Palatino-Bold
    Palatino-BoldItalic
    Palatino-Italic
    Palatino-Roman

Papyrus
    Papyrus

Party LET
    PartyLetPlain

Snell Roundhand
    SnellRoundhand
    SnellRoundhand-Bold

Thonburi
    Thonburi
    Thonburi-Bold

Times New Roman
    TimesNewRomanPS-BoldItalicMT
    TimesNewRomanPS-BoldMT
    TimesNewRomanPS-ItalicMT
    TimesNewRomanPSMT

Trebuchet MS
    Trebuchet-BoldItalic
    TrebuchetMS
    TrebuchetMS-Bold
    TrebuchetMS-Italic

Verdana
    Verdana
    Verdana-Bold
    Verdana-BoldItalic
    Verdana-Italic

Zapf Dingbats
    ZapfDingbatsITC

Zapfino
    Zapfino

iPhone OS 3.1.3

American Typewriter
    AmericanTypewriter
    AmericanTypewriter-Bold

AppleGothic
    AppleGothic

Arial
    Arial-BoldItalicMT
    Arial-BoldMT
    Arial-ItalicMT
    ArialMT

Arial Hebrew
    ArialHebrew
    ArialHebrew-Bold

Arial Rounded MT Bold
    ArialRoundedMTBold

Arial Unicode MS
    ArialUnicodeMS

Courier
    Courier
    Courier-Bold
    Courier-BoldOblique
    Courier-Oblique

Courier New
    CourierNewPS-BoldItalicMT
    CourierNewPS-BoldMT
    CourierNewPS-ItalicMT
    CourierNewPSMT

DB LCD Temp
    DBLCDTempBlack

Geeza Pro
    GeezaPro
    GeezaPro-Bold

Georgia
    Georgia
    Georgia-Bold
    Georgia-BoldItalic
    Georgia-Italic

Heiti J
    STHeitiJ-Light
    STHeitiJ-Medium

Heiti K
    STHeitiK-Light
    STHeitiK-Medium

Heiti SC
    STHeitiSC-Light
    STHeitiSC-Medium

Heiti TC
    STHeitiTC-Light
    STHeitiTC-Medium

Helvetica
    Helvetica
    Helvetica-Bold
    Helvetica-BoldOblique
    Helvetica-Oblique

Helvetica Neue
    HelveticaNeue
    HelveticaNeue-Bold

Hiragino Kaku Gothic ProN
    HiraKakuProN-W3
    HiraKakuProN-W6

Marker Felt
    MarkerFelt-Thin

Thonburi
    Thonburi
    Thonburi-Bold

Times New Roman
    TimesNewRomanPS-BoldItalicMT
    TimesNewRomanPS-BoldMT
    TimesNewRomanPS-ItalicMT
    TimesNewRomanPSMT

Trebuchet MS
    Trebuchet-BoldItalic
    TrebuchetMS
    TrebuchetMS-Bold
    TrebuchetMS-Italic

Verdana
    Verdana
    Verdana-Bold
    Verdana-BoldItalic
    Verdana-Italic

Zapfino
    Zapfino
]]>
<![CDATA[Using RubyCocoa to alphabetically sort Property List files]]> 2010-02-26T00:00:00+11:00 http://forgecode.net/2010/02/using-rubycocoa-to-alphabetically-sort-property-list-files When you need small sets of static, structured data for your Mac or iPhone projects, the most common approach is to bundle a Property List (or plist) file in your app. Any object-graphs made solely of ‘property-list objects’ - i.e. instances of NSArray, NSDictionary, NSString, NSData, NSDate or NSNumber - can be easily converted to or from serialised data formats, which means that they can be trivially read from or written to files or network sockets. For more information on plists, see Apple’s Property List Programming Guide.

Apple’s Xcode Developer Tools ship with a bunch of utility applications, including Property List Editor, which (as you might have already guessed) allows you to edit plists. One feature it doesn’t have is the ability to sort entries within NSDictionary objects alphabetically. You can re-arrange elements from the GUI, but for any non-trivial plist, sorting by hand becomes tedious. The order in which these entries appear is only of interest to a person editing the plist manually; as soon as you read the plist using the Cocoa APIs, any order is lost, since NSDictionary objects have no concept of sort order. As an aside, if you do want an ordered NSDictionary or NSMutableDictionary, check out Matt Gallagher’s post on Cocoa with Love: OrderedDictionary: Subclassing a Cocoa class cluster.

RubyCocoa = Mac OS X Scripting Goodness

While I love working with Objective-C for application development, it’s not a good scripting language. For scripting, I prefer to use one of the highly dynamic, modern programming languages like Ruby or Python. RubyCocoa may not be the most technically advanced way of bridging the Cocoa APIs and the Ruby programming language (see MacRuby), but it does have the advantage that it ships with Mac OS X (from 10.5 onwards). Until MacRuby is shipped with Mac OS X, RubyCocoa is the best choice for scripting the Cocoa APIs in Mac OS X if you want to use Ruby.

The script I’ve written simply reads a plist from a file, then writes it back out again using the Cocoa APIs. When the plist data is written back to a file, the entries in each NSDictionary are sorted by their keys alphabetically, since this is the way that the Cocoa APIs serialise NSDictionary objects. The actual Ruby code for each step is extremely simple.

This loads RubyCocoa:

require 'osx/cocoa'

This will parse the contents of a plist at path into dict:

dict = OSX::NSDictionary.dictionaryWithContentsOfFile(path)

This will save the contents of dict to a file at path:

dict.writeToFile_atomically(path, true)

That’s all there is to it. The actual script that takes either one or two command line arguments. If you give only one argument, the script over-writes the original plist with the sorted version. If you give two, it reads from the path given in the first argument, and writes to the path given in the second.

The Actual Script

#!/usr/bin/ruby

require 'osx/cocoa'


infile = ARGV[0]
outfile = ARGV[1]

# If no output file is specified, over-write the input file
if outfile == nil
  outfile = infile
end

OSX::NSDictionary.dictionaryWithContentsOfFile(infile).writeToFile_atomically(outfile, true)

But That’s Kinda Boring…

Yeah, it is kind of boring. Sorting a plist file like this isn’t particularly useful for most people (well, it was useful enough for me to cause me to write the above script, but I digress). What might actually be useful for a lot of people would be to use RubyCocoa scripts to deal with plists as part of your project build cycle. If you are editing plists by hand, it may be useful to have a script that verifies that the static plist data files actually conform to the data structure your code is expecting; writing a RubyCocoa script to check this would probably be a lot easier than writing similar Objective-C code. It may also be useful to use simple scripts to populate your plist files with data from other sources, like a database, or a web API.

Whatever the reason, using RubyCocoa scripts instead of Objective-C to deal with plists programatically is a very handy skill in the arsenal of any Mac OS X or iPhone OS developer.

]]>
<![CDATA[Simplifying NSNumberFormatter use on the iPhone with Objective-C categories]]> 2010-02-02T00:00:00+11:00 http://forgecode.net/2010/02/simplifying-nsnumberformatter-use-on-the-iphone-with-objective-c-categories Something that comes up regularly in iPhone development is the need to convert NSNumber objects to NSString objects and vice versa. For converting an NSNumber to an NSString, there is the -stringValue method, which is useful if you want just the plain number without any extra formatting. This isn’t particularly useful for presenting numbers in a user interface however, since -stringValue doesn’t give you any control over how the number is formatted.

Enter NSNumberFormatter

The NSNumberFormatter class is Cocoa’s way of converting between NSNumber and NSString objects. It provides both printing (number-to-string) and parsing (string-to-number). NSNumberFormatter offers a number of built-in “formatter styles”; these are pre-built styles that allow you to print or parse numbers in a number of common styles. The different styles are declared in an enum as the type: NSNumberFormatterStyle. Here are the different formatter styles, and the result when converting [NSNumber numberWithDouble:123.4] into an NSString.

  • NSNumberFormatterNoStyle (123)
  • NSNumberFormatterDecimalStyle (123.4)
  • NSNumberFormatterCurrencyStyle ($123.40)
  • NSNumberFormatterPercentStyle (12,340%)
  • NSNumberFormatterScientificStyle (1.234E2)
  • NSNumberFormatterSpellOutStyle (one hundred and twenty-three point four)

Note that my iPhone and Mac have the country set to Australia, and the language set to Australian English. These built-in styles are localised, so the results may be different depending on the country and language of your iPhone (or Mac, if you’re running in the iPhone Simulator).

NSNumberFormatter In Practice

To actually convert an NSNumber to an NSString using NSNumberFormatter using one of the pre-defined NSNumberFormatterStyle options (in this case, NSNumberFormatterCurrencyStyle), you would use the following code:

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
NSString *string = [formatter stringFromNumber:number];
[formatter release];

Simple enough, but every time I write code that does this, a little voice in the back of my head says:

You repeat those 4 lines of code all the time. Write a category on NSNumber, stick those 4 lines in a category method, beautify your code, and save the world!

Right. The following methods could be written in a category on NSNumber, and the world would be a much better place:

- (NSString *)stringWithNumberStyle:(NSNumberFormatterStyle)style;

+ (NSNumber *)numberWithString:(NSString *)string
                 numberStyle:(NSNumberFormatterStyle)style;

Then we could do the conversion mentioned earlier as easily as this:

NSString *string = [number stringWithNumberStyle:NSNumberFormatterCurrencyStyle];

We would have turned 4 lines of code into 1 easy to read line of code, which is clearly a lot nicer than the “normal” way of using an NSNumberFormatter.

Then another voice in my head says:

But that means we’ll be creating, configuring and destroying an NSNumberFormatter every time we use these methods! That’ll be inefficient, right?

Instead of seeking medication for the voices I seem to be hearing in my head, I decided to write up the categories and do some testing to see just how inefficient it would be.

I ended up with 4 different versions of my category methods. I wrote a test app that timed how long it took to perform the same conversions with each version, then collected the data to compare the efficiency of each version.

The Benchmark

To calculate the “efficiency” of each of my different approaches, I needed a “best practice” benchmark. I used code resembling the following to do a large number of conversions in the most efficient way possible:

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterPercentStyle;
for (int i = 0; i < TEST_ITERATIONS; i ++) {
  NSNumber *num = [NSNumber numberWithInt:i];
  NSString *str = [formatter stringFromNumber:num];
}
[formatter release];

Testing this code with TEST_ITERATIONS equal to 10,000 revealed that on average, each number-to-string conversion took approximately 113.7µs on my iPhone 3GS. This was my benchmark - now for the alternative methods.

Version 1 - Dumb

Version 1 is the most simple (and obvious) way to solve this problem:

- (NSString *)stringWithNumberStyle1:(NSNumberFormatterStyle)style
{
  NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
  formatter.numberStyle = style;
  NSString *string = [formatter stringFromNumber:self];
  [formatter release];
  return string;
}

To do the same conversions as the benchmark, I used code like this:

for (int i = 0; i < TEST_ITERATIONS; i ++) {
  NSNumber *num = [NSNumber numberWithInt:i];
  NSString *str = [num stringWithNumberStyle:NSFormatterPercentStyle];
}

The obvious inefficiency here is that every time we call the method, we create, configure, then destroy an NSNumberFormatter object. If we’re using this method a lot, this might start to add up.

Testing showed that this method took 2741.1µs per conversion, which is around 24 times slower than the benchmark. If you need to do more than 100 of these conversions at a time, this adds up to a quarter of a second, which is definitely a noticeable delay (this compares to only 10ms for the benchmark). I decided once I saw these figures that it was worth optimising the code to see how fast we could get it to go.

Version 2 - Smart?

Version 2 caches a single NSNumberFormatter object in a global variable (effectively a singleton), so we don’t have to create and destroy an object every time we use our category method.

NSNumberFormatter *sharedNumberFormatter = nil;
NSString *kSharedNumberFormatterLock = @"kSharedNumberFormatterLock";

...

- (NSString *)stringWithNumberStyle2:(NSNumberFormatterStyle)style
{
  NSString *string = nil;
  @synchronized(kSharedNumberFormatterLock) {
      if (sharedNumberFormatter == nil) {
          sharedNumberFormatter = [[NSNumberFormatter alloc] init];
      }
      sharedNumberFormatter.numberStyle = style;
      string = [sharedNumberFormatter stringFromNumber:self];
  }
  return string;
}

This time, the only difference between our category method and the benchmark is that we have to run through a mutex lock every time, and we are re-configuring the NSNumberFormatter every time we use it. How much does this slow us down? It turns out that this code averages 1466.2µs per conversion, which works out to be nearly twice as fast as version 1, but still 13 times slower than the benchmark.

Version 3 - Smarter

It turns out that re-configuring the NSNumberFormatter is slow. By maintaining a separate NSNumberFormatter for each of the NSNumberFormatterStyle styles, our efficiency starts to approach the benchmark.

NSNumberFormatter *sharedNumberFormatterDecimalStyle = nil;
NSNumberFormatter *sharedNumberFormatterCurrencyStyle = nil;
...

static NSString *kSharedNumberFormatterDecimalStyleLock = @"kSharedNumberFormatterDecimalStyleLock";
static NSString *kSharedNumberFormatterCurrencyStyleLock = @"kSharedNumberFormatterCurrencyStyleLock";
...

- (NSString *)stringWithNumberStyle3:(NSNumberFormatterStyle)style
{
  NSNumberFormatter *formatter = nil;
  switch (style) {
      case NSNumberFormatterDecimalStyle:
          @synchronized(kSharedNumberFormatterDecimalStyleLock) {
              if (sharedNumberFormatterDecimalStyle == nil) {
                  sharedNumberFormatterDecimalStyle = [[NSNumberFormatter alloc] init];
                  sharedNumberFormatterDecimalStyle.numberStyle = NSNumberFormatterDecimalStyle;
              }
          }
          formatter = sharedNumberFormatterDecimalStyle;
          break;
          
      case NSNumberFormatterCurrencyStyle:
          @synchronized(kSharedNumberFormatterCurrencyStyleLock) {
              if (sharedNumberFormatterCurrencyStyle == nil) {
                  sharedNumberFormatterCurrencyStyle = [[NSNumberFormatter alloc] init];
                  sharedNumberFormatterCurrencyStyle.numberStyle = NSNumberFormatterCurrencyStyle;
              }
          }
          formatter = sharedNumberFormatterCurrencyStyle;
          break;
          
      ...

      default:
          break;
  }
  return [formatter stringFromNumber:self];
}

Note that I have removed the other styles from this code to aid in readability; where you see ‘…’, there is in fact duplicate code for the remaining formatter styles.

Version 3 clocks in at 127.8µs per conversion, which is only 12.4% longer than the benchmark. This is what we would expect - the only things that should be slowing us down (explaining the 12% speed penalty) are the mutex locks.

Version 4 - Smartest!

In version 4, I added an if statement that skips the mutex lock if the appropriate sharedNumberFormatter has already been initialised. An if statement is a whole lot faster than a @synchronized statement, so this is another win.

- (NSString *)stringWithNumberStyle4:(NSNumberFormatterStyle)style
{
  NSNumberFormatter *formatter = nil;
  switch (style) {
      case NSNumberFormatterDecimalStyle:
          if (sharedNumberFormatterDecimalStyle) {
              formatter = sharedNumberFormatterDecimalStyle;
              break;
          }
          @synchronized(kSharedNumberFormatterDecimalStyleLock) {
              if (sharedNumberFormatterDecimalStyle == nil) {
                  sharedNumberFormatterDecimalStyle = [[NSNumberFormatter alloc] init];
                  sharedNumberFormatterDecimalStyle.numberStyle = NSNumberFormatterDecimalStyle;
              }
          }
          formatter = sharedNumberFormatterDecimalStyle;
          break;
          
      ...
          
      default:
          break;
  }
  return [formatter stringFromNumber:self];
}

Again, I’ve removed the code that handles the other formatter styles for readability (I only included the switch/case for the Decimal Style). This version clocks in at 116.3µs, which is only 1.02 times as long as the benchmark (2.3% to be precise). There’s clearly little point in trying to optimise this any further - this is about as fast is we’re going to get.

Number-to-String results

Here are the results for Number-to-Strong conversions. I generated these figures by running the tests multiple times, discarding any obvious outliers, then taking averages. If anyone is interested in seeing the code used to generate these numbers, let me know and I’ll post the whole Xcode project.

Average (µs) Penalty (%)
Benchmark 113.7 -
Version 1 2741.1 2311%
Version 2 1466.2 1190%
Version 3 127.8 12.4%
Version 4 116.3 2.3%

String-to-Number results

Writing string-to-number methods was relatively straightforward after writing the number-to-string methods. The actual structure of each method was exactly the same, so the hard work was already done.

I also generated results for the string-to-number conversions:

Average (µs) Penalty (%)
Benchmark 348.2 -
Version 1 2909.2 735.5%
Version 2 1664.7 378.1%
Version 3 369.0 5.9%
Version 4 359.7 3.3%

Conclusion

If you want to use any of the pre-built NSNumberFormatterStyle options, the NSNumber category methods that I have described here give you a much nicer syntax, with a negligible performance hit. If you need to use a non-standard formatter style, then clearly these methods won’t help, and you’ll need to use an NSNumberFormatter directly. Creating, configuring and destroying each NSNumberFormatter instance is expensive compared to the time it takes to actually do the conversion; if you ever have to use an NSNumberFormatter with the same settings more than a couple of times, hold on to it (e.g. in an ivar).

Download

I’ve uploaded the source code for all 4 versions for both -stringWithNumberStyle: and +numberWithString:numberStyle:. I will publish a polished version of the “Version 4” methods on GitHub once I’ve given the code a proper test-drive.

Download the Source Code

]]>
<![CDATA[A WordPress plugin for the Mint Bird Feeder Pepper (mint-bird-feeder)]]> 2010-01-29T00:00:00+11:00 http://forgecode.net/2010/01/a-wordpress-plugin-for-the-mint-bird-feeder-pepper-mint-bird-feeder If you use Shaun Inman’s Mint stat tracking software on a WordPress based site, as I do on Forge Code, you probably use the Bird Feeder Pepper. This ‘Pepper’ - the name given to Mint plugins/widgets - tracks the number of hits on your RSS feeds. The normal way to get Bird Feeder to collect this data is to manually modify the content of the PHP template file(s) in WordPress that are used to generate the content of your RSS feeds.

Modifying core WordPress files is fraught with issues - primarily the fact that whenever you upgrade WordPress itself, there’s a good chance that any changes you’ve made will get nuked. Writing a WordPress plugin is a much more robust way to achieve the same thing, which avoids all of the issues with modifying the WordPress core. I have written a very simple plugin for WordPress that allows the Mint Bird Feeder Pepper to gather stats from your RSS, RSS2 or Atom feeds without any configuration - just install the plugin, and activate it. It only handles “post feeds” (not “comment feeds”), but if that’s all you’re interested in, this plugin should come in handy.

Here’s a link to the WordPress Plugin Directory page for the plugin.

]]>
<![CDATA[Making Xcode Work For You When Developing PrefPanes]]> 2009-11-18T00:00:00+11:00 http://forgecode.net/2009/11/making-xcode-work-for-you-when-developing-prefpanes The first few times I tried developing a Preference Pane in OS X, I had more than a few head-scratching, “what the heck do I do now?” kind of moments. Unlike a typical Mac OS X or iPhone project in Xcode, when you create a Preference Pane project from the built-in Xcode template, doing a ‘Build and Run’ doesn’t result in your project being run. In fact, as of Xcode 3.2.1 the ‘Build and Run’ item in the ‘Build’ menu is disabled ‘out of the box’. This is due to the fact that your project isn’t building an executable program, it’s building a .prefPane bundle/plug-in.

Getting ‘Build and Run’ (⌘+R) to work as you might expect for a PrefPane project (by building your .prefPane, then installing it in System Preferences.app and loading it) isn’t hard, but it’s not at all well documented.

My First Attempt

When I started working on my first PrefPane as a learning around two years ago, I wrote a macro in Keyboard Maestro that would fire when I pressed ⌘+R in Xcode. The macro would open my freshly built .prefPane (which makes System Preferences.app load it), wait 1 second, then hit the ‘Return’ key, which would install the new .prefPane by dismissing the following dialog:

This worked, but it had a lot of downsides. Firstly, I had to turn this macro on and off every time I changed from working on this particular PrefPane project to working on anything else so that ⌘+R would do what it should for other projects. Secondly, it didn’t allow me to debug the PrefPane, only run it. This is a serious impediment to development, especially if you don’t know what you are doing (which, at the time, I didn’t!).

When I returned to looking at PrefPanes for another project about a year ago, it occurred to me that there must be a better way…

A Better Way

To get Xcode to actually ‘Run’ something, you need to have an ‘Executable’. Since a PrefPane doesn’t generate an Executable (something that you can actually execute as a program), you’ll need to add a ‘Custom Executable’ to your Xcode project. To do this, go to the ‘Project’ menu, and select ‘New Custom Executable…’.

Call the Custom Executable ‘System Preferences’, and select the ‘System Preferences.app’ application in /Applications/ by clicking on the ‘Choose…’ button.

You can now Run and Debug System Preferences.app by pressing ⌘+R and ⌘+Y. That’s great, but it doesn’t install your .prefPane as part of the process, it just launches the System Preferences.app application.

System Preferences.app’s Hidden Command Line Argument

I found this out through trial and error, and I haven’t seen it documented anywhere, but the System Preferences.app application actually takes a command line argument, which is the path of a .prefPane to install.

To set this argument, go to the ‘Project’ menu, then select ‘Edit Active Executable “System Preferences”’. Click on the ‘Arguments’ tab at the top of this pane, then add a new command line argument that is the path of your built .prefPane bundle.

/Users/nick/Builds/Xcode/Debug/MyPrefPane.prefPane is the path to the .prefPane built for the MyPrefPane project when it is built in the ‘Debug’ configuration on my machine. Note that on my Xcode setup, I have a customised location for build products (/Users/nick/Builds/Xcode), so even if I move my PrefPane project folder around, this path won’t change. If you have Xcode setup to build in MyProject/build (which is the default), you’ll have a path that looks more like /Users/nick/Code/MyPrefPane/build/Debug/MyPrefPane.prefPane. If you move or rename your Xcode project folder, this path will change, and your project will break, so beware.

Also of note: this technique brings up the dialog shown above asking if you want to replace (or install, if it’s the first time) the .prefPane in question whenever you do a ‘Run’. Hitting ‘Return’ after a build isn’t a huge deal to me, but it would be nice to have this automated. Another command line argument for System Preferences.app could help here, but I’m not aware of the existence of such an option.

Why Doesn’t The Xcode Template Do This For Me?

There’s no good reason that I can think of why the default template doesn’t include System Preferences.app as a Custom Executable. There is, however, a good reason it doesn’t include the .prefPane path as a command line argument. The path is dependent on your Xcode configuration, and the path is fragile (by which I mean that if you renamed your Xcode project folder, “Run” would no longer install your .prefPane).

For Apple (or anyone else, for that matter) to make a template do all this for you, they’d have to allow the Executable ‘Argument’ fields to support Xcode variables. As of Xcode 3.2.1, I have been unable to work out a way to do this. In some fields in Xcode, you can use variables like $BUILT_PRODUCTS_DIR and $PRODUCT_NAME, to represent (not surprisingly), the Build Products Directory, and the Product Name of your current project. If the ‘Argument’ fields supported these variables, it should be possible to use something like $BUILT_PRODUCTS_DIR/$PRODUCT_NAME.prefPane as a command line argument, which would not only be robust against configuration and path changes, but would mean that the setup I have described here could be built in to the Xcode Preference Pane project template itself, meaning that things would ‘just work’ out of the box.

Addendum

As pointed out by Rainer Brockerhoff in the comments, Xcode does support variables in Executable Arguments. The real problem is that for some reason $PRODUCT_NAME doesn’t work when using the default Xcode PrefPane template. Investigations continue…

]]>
<![CDATA[Scripting iTunes with Ruby, and Snow Leopard's Apple Events Bug]]> 2009-10-16T00:00:00+11:00 http://forgecode.net/2009/10/scripting-itunes-with-ruby-and-snow-leopards-apple-events-bug About 2 weeks ago, I made a post to the rb-appscript-discuss mailing list about an error I was receiving while working with rb-appscript. rb-appscript is an event bridge that allows you to use Ruby instead of AppleScript to communicate with scriptable applications on Mac OS X.

As a programmer, I find AppleScript tedious at best. For better or worse, my brain has adapted to understanding programming languages that have non-ambiguous, concise grammars. I find it a lot easier to use a “real” programming language, and luckily enough, in 2009, we can use a number of languages as an AppleScript substitute, including Ruby.

Since I don’t actually know much about AppleScript or how it works, I didn’t appreciate the fact that you can use AppleScript to request data, with a logical set of constraints, and that the constraints will be filtered within the client app. This means that you can do the following with iTunes (this is pseudo-code, not AppleScript):

get all tracks where the track length is greater than 300 seconds,
    and the track length is less than 310 seconds
    and the artist is "Radiohead"

This can be achieved in rb-appscript using the following:

app('iTunes').playlists[1].tracks[its.duration.gt(300).and(
                                its.duration.lt(310)).and(
                                its.artist.eq("Radiohead"))].get

That line of code takes about 0.2 seconds to execute on my 2GHz Core Duo MacBook, and I have an iTunes library of around 5000 songs. When I started working with rb-appscript, I didn’t realise this was possible, and I was using Ruby enumerables to do my data filtering like so (this example finds all songs by Radiohead):

app('iTunes').playlists[1].tracks.get.select { |track|
  track.artist.get == 'Radiohead'
}

This line of code takes around 2 seconds to execute. There’s a number of reasons that it’s so slow, but a major reason is that for every track in the iTunes library, a separate Apple Event is being sent to iTunes to request its ‘artist’. The script I was working on (which I’ll describe in detail in a later post) was doing quite a bit of data filtering, which when done in this fashion using Ruby enumerables, was sending a crap load of Apple Events. Occasionally, on my Mac Pro (which has 10.6.1 installed), my script would freeze, and I’d get the following message:

OSERROR: -1712
MESSAGE: Apple event timed out.

I did a bit of testing, and discovered that the same script would not freeze on any machine running 10.5. I made a post to the rb-appscript-discuss list, and after following Matt Neuburg’s suggestion that it might be caused by a Scripting Addition, I removed /Library/ScriptingAdditions/Adobe Unit Types.osax, and restarted, which seemed to fix the problem. Although everything seemed to be working again, I had a nagging feeling that perhaps the restart was what “fixed” things.

I was able to work for a few days without any crashes, until eventually my script froze while attempting a particularly long sequence of data requests. I posted to the rb-appscript-mailing list again, and after describing in more specific detail what was happening, was able to get Matt Neuburg to reproduce the bug also. Given that the freeze would only happen after a large number of Apple Events, Matt had the insight to write a script that would count the number of Apple Events before the freeze occurred. He mentioned that the “crash count” for him was a number close to 65535 (216 - 1, which is the largest number you can represent in an unsigned 16-bit integer). I ran his script, and initially got a number around 47000. After quite a bit of experimenting and collecting of data, I discovered the following things:

  • Restarting the computer would reset the crash count to something near 65535
  • Every time a crash occurred, the next crash count would be slightly smaller
  • The crash count was independent of the process you send Apple Events to, it was system-wide

After reporting this information to Matt on rb-appscript-discuss, he was able to get in touch with the right people, and was able to diagnose the exact nature of the problem. It looks like Apple may even be rolling a bug-fix into 10.6.2.

If I had actually known what I was doing with AppleScript or rb-appscript when I started working on this particular project, I never would’ve encountered this bug, and it may have stayed lurking in the Snow Leopard source code until someone else inadvertently “stress tested” the Apple Events sub-system.

Matt has a full write-up on the bug in his comprehensive article over at TidBITS, which explains part of the Apple Events mechanism and the actual bug itself.

]]>
<![CDATA[Travelling]]> 2009-08-18T00:00:00+10:00 http://forgecode.net/2009/08/travelling I’m writing this post from my iPhone here in Amsterdam. I’ve been travelling with Catherine through Europe for the past week and a half, which is something we’ve dreamed about doing for years now. We’ve just been to England, are now in the Netherlands, and we’ll be travelling through Germany, the Czech Republic, Austria and Italy before heading home to Melbourne. Also on the cards is the Lowlands music festival here in the Netherlands, which should be an awesome way to experience a different side of Dutch culture.


"Brighton Beach, England"

Brighton Beach, England


I’m taking a complete break from all software related work while we’re away, so you’ll have to wait until I get back (about a month from now) for any technical content. In the meantime, I might occasionally post a thing or two about the places we’ve visited and the things we’ve seen.

Until my next post, Goedendag!

]]>
<![CDATA[Exams]]> 2009-06-21T00:00:00+10:00 http://forgecode.net/2009/06/exams

If you’re reading this, you may have noticed that there’s been something of a lull in posts on Forge Code over the past month or so. WWDC 2009 landed squarely in the middle of my final exam period.

Usually at The University of Melbourne, we have a week of Swotvac to prepare for exams, but due to my trip, this study period was effectively reduced to nothing. This has meant that I’ve had very little time for things like writing here, since I’m madly trying to cram for my last ever exams!

In the meantime, I’ll just keep doing my best impression of Fry from Futurama:

“Of course I’ve been up all night! Not because of caffeine. It was insomnia. I couldn’t stop thinking about coffee. I need a nap…

…Coffee time!”

]]>
<![CDATA[At WWDC 2009]]> 2009-06-10T00:00:00+10:00 http://forgecode.net/2009/06/at-wwdc-2009 Two days gone, three to go. WWDC 2009 has been great so far. I’ve met some really interesting people, and have finally adjusted my body clock to SF time!

After getting down to Moscone West at around 5am on Monday morning, I managed to score a seat pretty close to the front for the Keynote:

The new 13” MBP looks like an awesome developer laptop, and has now definitely moved to the top of my wishlist! Of note to Australians is the fact that Telstra didn’t appear on the list of carriers supporting tethering right away, which didn’t really surprise me. Hopefully they will rectify the situation in the coming months without ripping off their customers too much.

There’s some really impressive stuff in Snow Leopard: the most interesting topics so far have been in the area of compiler and Xcode technologies. Apple are making important moves in the right direction as far as I’m concerned. I’d love to talk about this in more depth, but I’ll have to wait until Snow Leopard is released and the NDA is lifted.

The next three days look really great, with the sessions promising to get much more technical and in-depth, which is the part of the week that I really look forward to!

]]>
<![CDATA[Palm's WebOS 'Media Sync' iTunes Integration Revisited]]> 2009-06-06T00:00:00+10:00 http://forgecode.net/2009/06/palms-webos-media-sync-itunes-integration-revisited I arrived in San Francisco this afternoon on my trip to WWDC 2009. The weather is great, I’m very jet-lagged after the flight(s) from Melbourne to SF, and I’ve finally had a chance to respond to the ongoing debate on Palm’s WebOS ‘Media Sync’ iTunes integration.

In response to my last post, Gruber wrote:

I disagree with Forge that an analogy to Microsoft and SMB is apt. Being the clear market leader doesn’t necessarily mean that Apple holds a monopoly, a term which has been thrown around far too loosely in the aftermath of Microsoft’s court cases in the U.S. and E.U. Many markets have a clear leader, but very few market leaders hold a monopoly.

The EU doesn’t refer to Microsoft as having a ‘monopoly’ in most of the literature regarding the recent EU Microsoft Antitrust Case, they refer to the abuse of a dominant market position. The fact that the iPod doesn’t have a technical monopoly doesn’t mean that they don’t have what the EU would consider a dominant market position, which is apparently enough to pursue antitrust proceedings.

What monopoly does Apple hold, specifically? A monopoly in “portable media players” wouldn’t seem relevant — it is iTunes, the Mac and Windows software, that WebOS interfaces with, not iPods. Does iTunes (not the store, but the desktop app) constitute a monopoly? I would say no, not even close.

I would argue that when you buy an iPod, you aren’t just buying the hardware. You are buying the functionality that you get from the combination of an iPod and iTunes. And an iPod with iTunes is indisputably the market dominant combination of portable media player and media library software.

Does the fact that Apple gives away the iTunes part of that combination for free mean that iTunes itself is immune from anti-competitive regulation?

From another angle, it would be hard to argue that iTunes doesn’t have a monopoly (or at the very least, a dominant market position) in music playing software itself on OS X. Microsoft’s recent brush with the EU was related to their bundling of Windows Media Player with Windows - it isn’t hard to see how the EU could potentially take issue with Apple aggressively breaking compatibility between iTunes and 3rd party players.

I’m not a lawyer, so I have no idea of the legal validity of these arguments, but I don’t think the issue is as black and white as many people seem to think.

]]>
<![CDATA[Why Palm's WebOS 'Media Sync' iTunes Integration CAN Be Legit]]> 2009-06-02T00:00:00+10:00 http://forgecode.net/2009/06/why-palms-webos-media-sync-itunes-integration-can-be-legit Gruber writes:

In terms of legal risk, this move almost makes me think that Palm is trying to provoke Apple into filing a lawsuit. The danger for Palm in such a suit is with all of the former Apple engineers now working for Palm. (There are many.) Did they use inside knowledge of the iPod/iTunes USB interface to implement the WebOS “media sync” feature? Palm’s not stupid — or at least Jon Rubinstein is not — so I would wager that Palm was careful to “clean-room” reverse-engineer the protocol. But if Apple sues, Palm would be forced to prove this in court, and in the meantime, they could be faced with the public perception that they’ve stolen Apple’s IP.

It’s one thing for Apple not to facilitate syncing with 3rd-party (non-Apple) players, but another altogether to actively go out of their way to stop it happening. Can you imagine the fallout if Microsoft were to add code into the SMB protocol that blocked access to non-Microsoft systems?

Apple effectively monopolises the portable media player market, and if they resort to Microsoft-in-the-90s style tactics to hold on to that monopoly, they could find themselves on the wrong side of the US Department of Justice.

I suspect Apple won’t take any “official” action to prevent Palm’s reverse-engineered syncing, since that would be anti-competitive. What they might do is release an iTunes software update that “accidentally” breaks the Palm syncing engine. If this were to happen, it would be extremely difficult for Palm to prove, in a legal sense, that Apple had deliberately broken compatibility. From an ethical point of view however, there would be no ambiguity.

]]>
<![CDATA[Creating the Interface for Bjango's Beats]]> 2009-05-27T00:00:00+10:00 http://forgecode.net/2009/05/creating-the-interface-for-bjangos-beats Beats is a cool iPhone app from fellow Aussie developers Bjango for DJs and musicians.

Marc Edwards has just posted an animation of their work on the UI, which gives a fascinating insight into designing a top quality iPhone app. The time lapse animation style communicates the design process better than any video or article I’ve seen.

Link to the Video

As usual for the Bjango/iSlayer guys, the final result is amazing. I particularly like the little spiral animation on the ‘7 segment display’ when entering Tempo or Metronome modes.

]]>
<![CDATA[Possible Fix for VPN Client Issues in OS X 10.5]]> 2009-05-08T00:00:00+10:00 http://forgecode.net/2009/05/possible-fix-for-vpn-client-issues-in-os-x-105 Most readers will probably find this post uninteresting - if you don’t know what a VPN is and aren’t interested in networking, tune out now!

OS X 10.5 comes with GUI configured clients for both PPTP and L2TP/IPSec VPNs. It also comes with a VPN server, in the form of the command line ‘vpnd’ program. Getting vpnd configured and running involves setting up a plist with the right settings and a few other small things. It’s possible to DIY, but if you want to save yourself the pain (and there can be a lot of pain involved), grab a copy of iVPN.

I have had countless issues getting the OS X 10.5 VPN clients to talk to the OS X 10.5 VPN server (vpnd), but after reading a tip on this Apple Support Discussions thread, I think I may have solved all of my remaining issues in one go (fingers crossed).

All I had to do was put the VPN client at the top of the “Service Order” list…

Setting the Service Order

To set the service order, go into your Network System Preferences pane, then click on the options “Gear Icon” at the bottom of the network devices list:

Now select “Set Service Order…”, drag your VPN(s) to the top of the list, hit OK, then hit Apply.

Hopefully, your VPN will now work as it’s supposed to!

Note that this tip will only make a difference if you can connect to your VPN, but are having problem accessing services on that network. If you can’t connect in the first place, the service order won’t make any difference.

]]>
<![CDATA[Forks in the Path of GUI Evolution]]> 2009-04-23T00:00:00+10:00 http://forgecode.net/2009/04/forks-in-the-path-of-gui-evolution Lukas Mathis at ignore the code on Oberon:

Steven Frank’s essay on the current state of the desktop UI reminded me of Oberon, a delightfully insane system I used back when I was studying computer science at ETH Zürich. The first thing you have to understand about Oberon is that it evolved entirely outside of the normal genealogy of user interfaces.

Most of the GUI paradigms nailed down by the Apple Lisa and Macintosh teams in the early ’80s are still with us today, for better or worse. Having been exposed to these paradigms from a young age (I had a Macintosh SE/30 at home when I was 3) it can be hard to think ‘outside the square’ of current GUI practice. When you consider the difficulty that Microsoft had in changing the UI for Microsoft Office 2007, and the resulting user backlash, it’s easy to see why things haven’t changed that much in the past 25 years. Having a large user base is both a curse and a blessing - any major changes to GUI paradigms means that at least some of your users will be pissed off.

In many ways, it’s easy to be jealous of those designing the new UI paradigms - the engineers designing the iPhone, the Palm Pre and Android. With what amounts to a ‘blank slate’, the designers of these platforms have been able to start from scratch to design platforms that make sense today, in 2009.

We shouldn’t view the extra legacy-support challenges of pushing UI boundaries on desktop computing platforms as being intractable. Rather, we should see them as a challenge of our ingenuity. And sometimes, the best sources of inspiration in an industry that is only 30 years old come from the very beginning of that short history.

]]>
<![CDATA[A Walk Down Operating System Memory Lane]]> 2009-04-21T00:00:00+10:00 http://forgecode.net/2009/04/a-walk-down-operating-system-memory-lane Gyorgy Fekete gives a great visual overview of the evolution of personal computer GUIs, going all the way back to the days of Xerox PARC in the ’70s:

Over the years a range of GUI’s have been developed for different operating systems such as OS/2, Macintosh, Windowsamiga, Linux, Symbian OS, and more.

We’ll be taking a look at the evolution of the interface designs of the major operating systems since the 80’s.

I should mention that this article showcases only the significant advances in GUI design (not operating system advances) and also not all of the graphical user interfaces and operating systems existing today.

(via UI and us)

]]>
<![CDATA[Accessing Gravatar with a Shell One-Liner]]> 2009-04-17T00:00:00+10:00 http://forgecode.net/2009/04/accessing-gravatar-with-a-shell-one-liner I’ve been playing around with different ways to use Gravatars recently. The basic concept of Gravatar is very simple - you upload an avatar to an account that is identified by your email address. The avatar can then be downloaded by anyone, anywhere, anytime as long as they have your email address. This allows any type of software that knows people’s email addresses to add a human touch the visual presentation of anything related to that person. Gravatar’s widest usage base is currently in blog comments, and most of the major blogging platforms already have native Gravatar support. The comments on this blog are Gravatar enabled - if you are wondering how to get a custom picture to show up in the comments section below, head over to the Gravatar website and set up a free account.

Accessing the Gravatar API

Using the Gravatar API (if you want to call it that) is very simple. The following URL will give you a JPEG image of my Gravatar.

http://www.gravatar.com/avatar/11c647cf66355ecc10a3a2c91e81e0a3

The jumple of numbers and letters at the end of the URL is an MD5 hash of my email address. Luckily for us, OS X 10.5 comes with md5, a command line utility for calculating MD5 hashes of files. To calculate the hash of a string, you can simply echo the string to stdout, then pipe that into md5. To calculate the MD5 hash for “me@mydomain.com”, you can use the following:

echo -n "me@mydomain.com" | md5

A few quick things to note here:

  • When calculating the MD5 hash for the Gravatar API, you have to use all lower-case letters in the email address.
  • The -n option of echo disables the trailing new-line character that echo normally spits out.

Download It Using curl

Now we can construct the Gravatar URL, and use curl to download it:

curl http://www.gravatar.com/avatar/`echo -n me@mydomain.com | md5` > gravatar.jpg

That’s all there is to it. When you run this command, a file named “gravatar.jpg” will be generated in your current directory that contains the Gravatar for “me@mydomain.com”. Substitute your own (or someone else’s) email address, and you’ll get the corresponding Gravatar. You can also request the Gravatar to be a certain size, or change the default Gravatar returned for an unknown email address by modifying the URL query as described here.

]]>