More of a programming nerd than is strictly healthy. See also {nevyn.nu, thirdcog.eu, twitter}

Projects

Overooped

Low Verbosity KVO

DRY Cocoa: SPLowVerbosity

I love Objective-C. It’s a very explicit language: there is no magic in the language. If you want such magic as distributed objects, model classes generated at runtime, generic change notification, or heck, even referenced counted memory management, you can implement it yourself in a library. It’s also by convention a very verbose language. If the method name doesn’t say exactly what the method does, the author is doing it wrong.

However, there are some things we type over and over again every day, whose verbosity does not make the code more readable but only serves to pad the code file with useless characters. These are in particular array and dictionary literals, and formatted strings.

ObjC wizard Jens Alfke wrote MYUtilities quite a while ago, and in particular CollectionUtils. This header defines a few very handy macros such as $array(...), $dict(...) and $sprintf(...). $array is simply a shorthand for [NSArray arrayWithObjects:..., nil] (note the extra nil, freeing you from typing that yourself every time, and also avoiding a possible crash). $dict is NSDictionary’s constructor, with the arguments in the right order (key, value, key, value, …). $sprintf is [NSString stringWithFormat:]. It may seem trivial, but this simple header has saved me tons and tons of typing, and surely from some silly bugs as well.

For more C++ friendliness, and to not have to depend on the rest of MYUtilities, I wrote my own, called SPLowVerbosity, with mostly the same things. The details are not interesting, so I won’t list the code; click through if you are curious.

This is a hack, and while it is very convenient, it is not pretty. Objective-C needs in-language literals for arrays, sets, dictionaries and number objects.

I do believe that such literals are now inevitable, though. With ARC, what I wrote in the first paragraph is no longer entirely true. Dealloc now magically calls super. Memory management is implicit and part of the language, not explicit and manual. The sanctity of Objective-C’s simplicity has been violated, parts of Cocoa has been moved into the language. Moving more parts of Cocoa into the language (e g exposing APIs saying ‘this is the class that should be instantiated when I use a dictionary literal’) no longer has a high religious price, and I bet we’ll see it at next year’s WWDC.

Object-Oriented KVO: SPKVONotificationCenter

During the past few years, my nose has started to really pick up on a code smell I didn’t feel before: unencapsulated concepts. What I mean by that is an API concept or contract that is only visible in comments and documentation, thus only implicit by knowledge of that documentation when you read code using the API, instead of exposing the concept as a construct the language can help you manage.

Examples:

Instead of the above concepts being implicit and only visible in documentation, several of them can be represented as objects and closures, thus making it impossible to do wrong.

The realization is not new, of course. It’s quite common to use RAII patterns in C++ to encapsulate concepts in code, giving them an explicit start point and a managed (and deterministic) end point.

But this is Objective-C, not C++, so we don’t have RAII. We do have objects, however, and if we use them to actually represent our objects, we can clean up so much code.

SPKVOObservation is an object that represents a KVO subscription. When this object is created, you know you have your subscription. When this object disappears, you know the subscription is gone. By managing instances of this class like I would any other instance variable, I know I’m following KVO’s subscribe/unsubscribe contract. (With ARC, I don’t even have to manage the ivar).

SPKVONotificationCenter also has dispatch to a given selector instead of the catch-all -[NSObject observeValueForKeyPath:ofObject:change:context:].

(As for solutions for the rest of the examples: a closure can be used for the mutex case if in the current scope, otherwise you might want RAII (which is possible with GNU extensions even in C). For the table view, I’d prefer bindings, but doing to-many KVO and bindings can be really hard, so I doubt we’ll be seeing it on iOS in the near future.)

DRY KVO: SPDepends

I love KVO. Basically every library I’ve ever used has their own implementation of notifications on attribute changes. Such an implementation is of course not difficult, but because there is no generic system for it in other languages/platforms, you can’t build a framework for working with change notifications generically. Recognizing that this is a generic concept, and then as good as building it into the language is genius, and has given us wonders such as bindings.

Outside of their use in bindings where you have tool help, KVO’s API is pretty bad, and working with it is almost painfully verbose (made worse by the single callback point, spreading your code out all over the file). What I really wanted to do was to unify the two above sections for a very simple, non-verbose and object oriented approach to KVO. The result is SPDepends. Magic is taken to the next level; we are approaching dark arts. Whether what I’m about to present is actually a good idea or not, I leave as an exercise for the reader. First, the header:

tl;dr: SPDepends lets you give it a list of {object, key path(s)} pairs that one of your properties depend on. When the value of any of the given key paths changes, a given closure is called, letting you recalculate the dependent property (or whatever you want to do). It’s about as far as you can go with KVO without actually building bindings.

There is one additional piece of magic related to memory management (this was before ARC). By providing an owner and associationName, the dependency is assigned as an associatedObject, so that it will automatically be cleaned up when the owner object dies. Defining a dependency again with the same name will also throw away the old dependency. If this is not desired, you can just not provide the association name, and instead manage the returned SPKVOObservation object on your own.

Example:

Output:

Note how short the $depends macro makes all your KVO work. The macro also defines a block-safe self variable selff that won’t create a reference cycle.

The equivalent classical KVO code would be many lines longer. However, this macro takes several steps away from how Cocoa code is normally written. Is the syntax too obscure? Is the code still readable for normal human beings?

Howto: Restore Springboard icon positions after iTunes botches them

You’re syncing from another computer. You’re restoring from a backup. iTunes has a bad day. Whatever reason, iTunes decided to resync all your apps, and replace them in your springboard in alphabetical order. I don’t know about you, but I don’t find that particularly helpful. I don’t know *how* many times I’ve meticulously sorted at least the three first Springboard pages with my most often used apps (which doesn’t sound like much, but *damn* does that UI suck!).

Expert instructions
Get your ~/Library/Preferences/com.apple.springboard.plist from your old device or a backup, and replace the plist on your new device or new install. Nothing of much importance is in that file, so it should be fine to just replace. If you’re worried, just open the two files in Property List Editor and copy the array over; it’s just an array of array of application identifiers under the keypath iconState.iconLists. Respring/`killall SpringBoard` when done.

Noobie instructions
Scenario: You have a recent backup of your phone/pod/pad in Time Machine. You have your device with the botched icon ordering. You want to fix things.

  1. Make a backup of your device. This way, we will know relatively certainly which backup belongs to this particular device.
  2. In Finder, navigate to your home folder > Library > Application Support > MobileSync > Backups
  3. Sort by Modified, newest at the top. The topmost one (something like “74e39ee0ac6b69af6f57e213cf5c6b95e5fd787f”, perhaps longer and with dashes) will be your device’s backup. Move this one to your desktop.
  4. Enter Time Machine for this folder. Restore the folder you moved away before to yesterday’s version or something
  5. Download the iPhone Backup Extractor app
  6. Open that app, click the button, choose the name of your device with the most recent date, and confirm
  7. At the bottom, there is an “iPhone OS Files” entry. Extract this to your desktop.
  8. Now, your device need to be jailbroken. If it isn’t already, get Spirit JB, run it, and jailbreak your device (it’ll only take seconds)
  9. If you just jailbroke your device, or if you don’t have SSH enabled, go into Cydia on your device, wait for it to restart, and then install OpenSSH. You must change your default password, or bad things will invariably happen.
  10. Get Transmit or another good SFTP client.
  11. Make sure you’re on the same wifi as your device.
  12. In your SFTP client, connect to (name of your device, with spaces replaced with dashes).local. My device is called Canvas, so I connected to Canvas.local. The connection method should be SFTP, the username should be ‘mobile’, and the password is your mobile password, as changed above or previously. By default it is ‘alpine’, but you REALLY SHOULD NOT USE THIS PASSWORD, as it means anyone can connect to your phone, and e g read and send SMSes.
  13. You’ll end up in your mobile user’s home folder. Navigate to Library > Preferences.
  14. From your desktop, get the file iPhone OS Files > Library > Preferences > com.apple.springboard.plist, and upload and replace the one on your device.
  15. Respring or reboot the device. Icon positions should be restored.

I haven’t tested the noobie instructions; just mail me at joachimb@gmail.com if they don’t work and we’ll figure it out.

 The write-compile-test cycle is very very tedious when doing iPhone development on the device, because the “compile” step needs to include “install on device”, which can be very, very slow. It can take up to a minute, depending on how many apps you have installed and the current cycle of the moon. Imagine, then, doing tiny interface changes and you want to see how that tiny fix changes the UI (which sometimes you really need to do on the device to get a feel for it) — ten tries changing an animation delay could mean ten minutes of just waiting for installation. If you’re jailbroken, there is an easier faster way.

In Cydia, install ldid, rsync and ssh
Follow this guide to install an ssh key pair on your iPhone, so that the script can install the app without asking for password.
Add an additional build target to your app, and call it “Upload” or something.
Make that build target depend on your real app (as in the picture above)
Add a “run shell script” build phase, and give it this script:
export DEVICE_NAME=Mishimazu.local
rsync -avz "${CONFIGURATION_BUILD_DIR}/${PROJECT}.app" root@${DEVICE_NAME}:/Applications/
ssh root@${DEVICE_NAME} ldid -s "/Applications/${PROJECT}.app/${PROJECT}"
Replace “Mishimazu” with the name of your iPhone.
Change your active target to “Upload”, and build as usual.
A few notes though.
This script does not launch the app, you’ll have to do that yourself.
You don’t get the console routed to Xcode. Open up the Console in the Organizer for a workaround (not as good though)
Xcode debugger won’t work
File locations might have changed! You no longer have your private uuid bundle with your documents, but rather need to place documents and related things in /var/mobile. It’s possible NSSearchPathForDirectoriesInDomains will figure the right paths out for you, I haven’t tested; just make sure you’re aware of this
You are no longer sandboxed. This might change assumptions you do in code


In short, only use this deployment method for simple things, and install as usual when you need to really make sure things still work as they should, before a beta or appstore deploy. Of course, if you’re targeting the Cydia store or similar, that doesn’t apply.

The write-compile-test cycle is very very tedious when doing iPhone development on the device, because the “compile” step needs to include “install on device”, which can be very, very slow. It can take up to a minute, depending on how many apps you have installed and the current cycle of the moon. Imagine, then, doing tiny interface changes and you want to see how that tiny fix changes the UI (which sometimes you really need to do on the device to get a feel for it) — ten tries changing an animation delay could mean ten minutes of just waiting for installation. If you’re jailbroken, there is an easier faster way.

  1. In Cydia, install ldid, rsync and ssh
  2. Follow this guide to install an ssh key pair on your iPhone, so that the script can install the app without asking for password.
  3. Add an additional build target to your app, and call it “Upload” or something.
  4. Make that build target depend on your real app (as in the picture above)
  5. Add a “run shell script” build phase, and give it this script:
    export DEVICE_NAME=Mishimazu.local
    rsync -avz "${CONFIGURATION_BUILD_DIR}/${PROJECT}.app" root@${DEVICE_NAME}:/Applications/
    ssh root@${DEVICE_NAME} ldid -s "/Applications/${PROJECT}.app/${PROJECT}"
  6. Replace “Mishimazu” with the name of your iPhone.
  7. Change your active target to “Upload”, and build as usual.

A few notes though.

  • This script does not launch the app, you’ll have to do that yourself.
  • You don’t get the console routed to Xcode. Open up the Console in the Organizer for a workaround (not as good though)
  • Xcode debugger won’t work
  • File locations might have changed! You no longer have your private uuid bundle with your documents, but rather need to place documents and related things in /var/mobile. It’s possible NSSearchPathForDirectoriesInDomains will figure the right paths out for you, I haven’t tested; just make sure you’re aware of this
  • You are no longer sandboxed. This might change assumptions you do in code
  • In short, only use this deployment method for simple things, and install as usual when you need to really make sure things still work as they should, before a beta or appstore deploy. Of course, if you’re targeting the Cydia store or similar, that doesn’t apply.