NSSortDescriptor objects. Here’s a quick example snippet:
|
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.
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.
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.
|
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:
|
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:
|
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:
|
You can wrap this up in a macro like this:
|
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:
|
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!
|
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:
|
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!
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!
]]>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…
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:
|
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!
]]>Now, onto the actual post…
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:
|
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.
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:
|
The app is freely available on github here: UIFont-Name-Grabber. Hopefully others will find it as useful as I do.
]]>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
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
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
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
]]>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.
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:
|
This will parse the contents of a plist at path into dict:
|
This will save the contents of dict to a file at path:
|
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.
|
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.
]]>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.
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.
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).
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:
|
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:
|
Then we could do the conversion mentioned earlier as easily as this:
|
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
NSNumberFormatterevery 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.
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:
|
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 is the most simple (and obvious) way to solve this problem:
|
To do the same conversions as the benchmark, I used code like this:
|
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 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.
|
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.
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.
|
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.
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.
|
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.
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% |
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% |
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).
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.
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.
]]>.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.
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…
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.
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.
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.
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…
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:
|
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):
|
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:
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.
]]>
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!
]]>
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!”
]]>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!
]]>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.
]]>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.
]]>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.
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.
]]>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…
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.
]]>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.
]]>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)
]]>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:
-n option of echo disables the trailing new-line character that echo normally spits out.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.
]]>