How to make iPhone apps and influence people

Musings on the iOS development process

The $33 IKEA Standing Desk

I was recently inspired by the sheer simplicity of $22 IKEA Standing desk by Colin Nederkoorn. The "Standdesk 2200" uses basic IKEA components to easily create a standing desk conversion for your existing space.

I set out this weekend to replicate and localise Colin's instructions for the Australian IKEA shopper. Colin's original $22 USD price point holds up perfectly in 2015. Converted to Australian dollars, we have the $33 IKEA Standing Desk.

All prices mentioned here are in Australian dollars and include 10% GST.

$33 IKEA Standing Desk


Total Cost: $33


I recommend reading Colin's article, as he highlights two main measurements; the keyboard height and the monitor height.

For my setup, I measured the distance from my elbow to the floor and assumed I'd be able to adjust the screen height to suit once I was standing at the desk. The distance from my elbow to the floor was almost exactly 1 meter.

Key Dimensions

  • Keyboard height: 1000mm
  • Lower desktop height: 725mm
  • Upper desktop height: 1175mm
  • Thunderbolt Display top: 1730mm
  • Desk depth: 755mm


  • Construct the LACK table as per the instructions and place it on top of your existing desk.
  • Take your elbow dimensions and work out at what point to screw in the brackets.
  • Place the shelf on top and screw it in place, allowing space on one side for your pointing device.


  • The legs of the top LACK table are hollow. Wood screws with long unthreaded shanks won't work with the hollow legs.
  • The legs on the LACK table don't meet the table top below evenly at 90 degrees, (probably because it's a $7 table). This makes the shelf sit unevenly on the brackets. I just use two screws to secure the shelf, one at each end. The shelf is completely stable even though it's not sitting 100% flush with the brackets on one end.

Nitty gritty details of the keyboard shelf Close up of the screws used $33 IKEA Standing Desk

Premium Upgrades

IKEA have a pricier versions of the LACK table and the EKBY shelf that are heavier (less hollow) with better finishes. I think these options are worth considering. At the store I felt the JÄRPEN shelf was much sturdier than the $5 cheaper ÖSTEN. I purchased the JÄRPEN shelf.

Total Premium Cost: $38-50

Full Office Fit Out

$33 IKEA Standing Desk

I realised that the remainder of my setup is also IKEA:

Other Accessories

Initial Standing Observations

  • Immediately, touch typing becomes essential when standing and looking forward. Looking down at the keyboard is slow.
  • Having the keyboard and mouse on a different level to other desktop peripherals makes a lot of sense.
  • The total depth of the desk is 755mm, up from 600mm which doesn't fit my room as well. There's about 100mm of wasted space behind the display.
  • MacBook Pro doesn't fit on the upper desktop, it will be a bit awkward plugging and unplugging it, especially if the cable falls behind the desk.
  • Cable runs are a bit longer for items that were previously closer to the thunderbolt display.
  • The flooring in my room is carpet, which is good for standing but creates minor wobbles when typing.
  • There is only 50mm difference between the depth of the top and bottom desks. With the LACK table currently sitting on top of my the other with no other support, this might be risky longer term. Having said that this feels quite sturdy with so much weight on the top shelf.

A photo posted by Jesse C (@sirjec) on

Profiling PHP Apps on OSX 10.9 with Xdebug and Kcachegrind

It's straight forward to generate Xdebug profiler output and visualise the bottlenecks associated with running any request that's run through the PHP stack.

Here's how you'd install PHP, Xdebug and Kcachegrind (qcachegrind) on OSX 10.9 Mavericks and get some in depth info on your php app.

qcachegrind on OSX 10.9 Mavericks

Install Homebrew

Make sure you have homebrew installed correctly.

The main thing to remember is to add /usr/local/ to your shell's $PATH. Often it's best to have it first so it overloads the system's default applications. brew doctor will complain if this isn't the case.

Add PHP Packages to Homebrew

PHP packages of OSX are not available to homebrew by default. The homebrew-php repository is the source of truth for PHP related recepies.

The homebrew-php README is verbose, on Mavericks you only need to run the following:

brew tap homebrew/dupes
brew tap homebrew/versions
brew tap josegonzalez/homebrew-php

This will give you access to all the php specific formulas.

Install PHP

Install PHP 5.3.x with the following commands:

brew install php53

Once installation is complete the formula tells you what to add to your httpd.conf to run the Take note of the location of the php.ini file. The system default location is /etc/php.ini.default.

You can get back to the post-install info of any homebrew package with brew info php53.

# To enable PHP in Apache add the following to httpd.conf and restart Apache:
#    LoadModule php5_module    /usr/local/opt/php53/libexec/apache2/
# The php.ini file can be found in:
#    /usr/local/etc/php/5.3/php.ini

Open your apache config at /etc/apache2/httpd.conf and replace the existing (possibly commented out) LoadModule php5_module line with the LoadModule line from above.

Restart the apache process with:

sudo apachectl restart


I like to add index.php as a DirectoryIndex option. Find the <IfModule dir_module> section of the apache config and add index.php after index.html.

<IfModule dir_module>
    DirectoryIndex index.html index.php

The arguments passed to the PHP compiler are visible in abstract-php.rb once you have everything running within apache the same variables are shown at the top of the phpinfo(); output under Configure Command.

The relevant options that will pick up the not-yet-installed XDebug config file and the php.ini file are:

def install_args
    args = [
    # ...
    # ...

Install Xdebug

Install the appropriate Xdebug version for php5.3

brew install php53-xdebug

The notes from the installation mention the location of the Xdebug config file.

# To finish installing xdebug for PHP 5.3:
#  * /usr/local/etc/php/5.3/conf.d/ext-xdebug.ini was created,
#    do not forget to remove it upon extension removal.

Restart apache and load up a phpinfo(); page and you should see references to Xdebug.

Configure Xdebug to Generate Profile Data

All Xdebug options are well documented, below are a minimum set of options to get profile data written.

Open the generated ext-xdebug.ini and three configuration options profiler_enable, profiler_output_dir and profiler_output_name.


xdebug.profiler_enable = 1
xdebug.profiler_output_dir = /Library/WebServer/xdebug-profiler
xdebug.profiler_output_name = "callgrind.%R.%t"
  • profiler_output_dir - can be any directory, but it must be writable by the httpd process that runs under the wheel user on OSX systems. By default /Library/WebServer is not writable by many users; so pick your directory appropriately.
  • profiler_output_name - this option can be a format of your choice, the line above will produce files based on the URL, suffixed by a timestamp (callgrind._bh_apiv1_cafes_371_json.1394929295)

When you change these settings, restart apache and the changes should be visible in phpinfo().

If you're not seeing logs straight away, it's more than likely a file permissions issue on the output directory.

Visualise Profile Data

The preferred way to install kcachegrind on OSX is to install qcachegrind and AppViz. Both components can be installed with homebrew.

Underneath the hood qcachegrind uses graphviz's dot application to generate the graphs. dot will be available to qcachegrind if you've setup your $PATH correctly above.

brew install qcachegrind
brew install graphviz

After installation you should have qcachegrind in your path and you can launch it with the profile files as the first argument.

qcachegrind callgrind._bh_apiv1_cafes_371_json.1394929295

The absolute worst misuses of localised strings

As a followup to posting my Localising Cocoa Apps talk I thought I'd outline some of my favourite localised string failures.

The "I heard literal strings in code are bad" kind of localisation

NSLocalizedString(@"Next", @"");
NSLocalizedString(@"Next", nil);

The "WTF is the comment for anyway" kind of localisation

NSLocalizedString(@"Next", @"Next");

The "out of sight out of mind" kind of localisation

#define MYBadCompanyLocalizedString(key) NSLocalizedString((key), @"")

Yes, that's redefining the macro to hide the comment...

The "entire book as a key" kind of localisation

NSLocalizedString(@"In order to determine your location, location services must be turned on in settings", @"In order to determine your location, location services must be turned on in settings");

The "key can be anything but I know what it is" kind of localisation

NSString *aStringFromAPI = [response objectForKey:@"next-title"];
NSLocalizedString(aStringFromAPI, @"api resposne for `next`");

The grey area

Duplicating keys

This is in the grey area because good distinct comments can make duplicating keys manageable as good comments show you the key's appearances in your app in the .strings file.

NSLocalizedString(@"Next", @"RootViewController 'next' button title");
NSLocalizedString(@"Next", @"DetailViewController 'next page' button title");

// DetailViewController 'next page' button title
// RootViewController 'next' button title
"Next" = "Next";
  • genstrings will warn you about duplicate keys
  • Bad/stupid/empty comments make this a bad offender
  • keys with nil or @"" comments aren't picked up as duplicate

Using localised strings in format strings

This is in the grey area because it's inflexible and implies a structure, but can be okay for small strings. Often you should consider NSNumberFormatter for numbers

button.titleLabel.text = [NSString stringWithFormat:@"4 %@, 3 %@", 
                          NSLocalizedString(@"pineapples",@"plural pineapples"),
                          NSLocalizedString(@"pears",@"plural pears")];

Localising the format string too

This is in the grey area because at this point we've got localised pieces everywhere. But it's good that you're trying though...

button.titleLabel.text = [NSLocalizedString stringWithFormat:
                          NSLocalizedString(@"4 %@, 3 %@", @"fruit quantities format string"),
                          NSLocalizedString(@"pineapples",@"plural pineapples"),
                          NSLocalizedString(@"pears",@"plural pears")];

Localising Cocoa Apps - Melbourne Cocoaheads August 2012

/* No comment provided by engineer. */
"Next" = "Next";

If your Localizable.strings file looks like this then you're probably doing it wrong...

Back in August I presented some best practices for localising your cocoa apps. The presentation centred around my experiences localising my own apps and the app. I highlighted how using NSLocalizedString properly (or it's more useful cousin NSLocalizedStringFromTable) can make localisation a lot easier and logical.

Localising Cocoa Apps - Melbourne Cocoaheads August 2012

The first part of the presentation touched on something most people in the audience could relate too... the most notorious misuses of NSLocalizedString that I've seen.

I then went onto explain how NSLocalizedString works.

Towards the end I touched on a custom implementation of NSLocalizedString I had been working on aptly named JCLocalizedString that had a compatible interface with NSLocalizedString but would allow switching the active localisation at run time. JCLocalizedString exists on github in it's early stages.

The presentation slides are up on github and the video is on vimeo. The presentation notes are included with the slides and may be a good reference.

Localising iOS Apps - Jesse Collis from Melbourne Cocoaheads on Vimeo.

'Obsidian Code' Xcode Theme

I made some adjustments to an Xcode theme that I've been using for the last twelve months or so.

Obsidian Code' Xcode Theme

Check out the gist