The Goo Software Blog

All The Goo That's Fit To Print

Solar Schools Success Story

Back last year I wrote the first version of the Solar Schools web app for the climate change charity, 10:10. Here’s a video that 10:10 have produced showing the great success they’ve had with Solar Schools since it launched. It’s so nice to see your work having a positive effect like this!

Twitter Bookmarklets for Hiding Replies and Retweets

When I visit someone’s profile on Twitter.com it’s generally because I want to see what they’ve written to me, the arbitrary reader. However, I often can’t see the wood for the trees; in a timeline with lots of replies or retweets it can be hard to weedle out the original tweets.

For example, here’s the current selection of above-the-fold tweets from the ever-readable Glenn Fleishman:

@GlennF timeline including replies

It’s all replies! That’s not to criticise Glenn. Quite the reverse; it’s great to see people having a two-way conversation on Twitter, it’s just that I’m not interested in this one – I don’t know the person he’s talking to and I only see half the conversation anyway.

I’d love it if Twitter had a toggle at the top of that page to turn display of replies and retweets on and off, but they don’t – so I wrote some myself. Below are two bookmarklets – drag them to your bookmark bar and they’ll toggle replies and/or retweets on and off accordingly, giving you something like this:

@GlennF timeline without replies

Much better!

Toggle replies bookmarklet

This bookmarklet toggles replies on and off when viewing someone’s timeline at twitter.com.

Toggle @s

Instructions: drag the link to your bookmarks bar, click to invoke.

Toggle retweets bookmarklet

This bookmarklet toggles retweets on and off when viewing someone’s timeline at twitter.com.

Toggle RTs

Instructions: drag the link to your bookmarks bar, click to invoke.

Source code

The bookmarklets aren’t perfect. You may need to re-invoke them on scrolling down the page as Twitter loads more tweets on demand. But they’re simple to install and use and suit the purpose I wanted them for.

Here’s an expanded version of the source for each bookmarklet, featuring self-documenting variable names, nice formatting and comments. Feel free to hack accordingly and improve the bookmarklets. Let me know if you’ve got improvements you’d like to share.

twitter-bookmarklets-expanded.js View Gist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
 *  === Routine for toggling @replies ===
 *
 *  Step 1: Using jQuery (which Twitter already loads), select the div 
 *  elements of class stream-item that contain tweets where the 
 *  data-is-reply-to attribute is true.
 */
var tweets = $('div.tweet[data-is-reply-to=true]').parents('div.stream-item');

/*
 *  Step 2: Determine whether we're hiding or showing. If there are no replies
 *  visible then we'll show replies, otherwise we'll hide them.
 * 
 *  FAQs :)
 * 
 *  Why not just call tweets.toggle()? 
 * 
 *  Because that gets all messed up if you scroll down and Twitter auto-loads
 *  some more tweets. At that point if you've got replies hidden then the
 *  bookmarklet will hide the visible tweets and show the hidden ones.
 *  (This way the bookmarklet also plays better with the one for hiding
 *  retweets where you have retweeted replies.)
 */
var shouldShow = tweets.filter(':visible').size() == 0;

/*
 *  Step 3: Toggle the replies on or off
 */
tweets.toggle(shouldShow);

/*
 *  === Routine for toggling retweets ===
 *
 *  Exactly the same logic as for replies, only in the first line we filter
 *  div.tweet elements based on whether they have a data-retweet-id attribute.
 */
var tweets = $('div.tweet[data-retweet-id]').parents('div.stream-item');
var shouldShow = tweets.filter(':visible').size() == 0;
tweets.toggle(shouldShow);

Calling UIDevice’s -uniqueIdentifier Without Warnings

If you use the TestFlight SDK in your iOS app you should be aware of a small but significant change in v1.0 which went live a couple of weeks ago. As this blog post explains, the TestFlight SDK no longer includes a device’s UDID by default in the data it reports back to the mothership. This means that, again by default, data on sessions, checkpoints etc in the TestFlight SDK console will no longer be associated with a specific tester’s device. The reason for the change is that the TestFlight SDK is intended for use on both test and production builds, and Apple will now reject apps that ask for a device’s UDID.

As a workaround, the TestFlight SDK now contains a new class method, +setDeviceIdentifier:, which you can use to add a device’s UDID to TestFlight’s callback packets in your Ad-Hoc builds by simply adding the following line to your code (I’ve added it right after my call to +takeOff:):

1
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];

(Note that you’ll need to wrap that in an #ifdef or similar to ensure that it only gets compiled in to test builds, not production ones.)

This works fine, but because UIDevice’s -uniqueIdentifier is now a deprecated method it generates a compiler warning. You’ll see something like this in your build logs:

/Users/simon/dev/personal-projects/apps/zippity/Zippity/ZPAppDelegate.m:70:38: 
    warning: 'uniqueIdentifier' is deprecated [-Wdeprecated-declarations]
    [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];

Urgh!

I hate compiler warnings, and eradicate them wherever possible. The reason is simple; if I ignore compiler warnings, I run the risk of missing something meaningful in my build logs. If my build logs perpetually show 20 warnings that I know about and have chosen to ignore, will I notice a new warning when it first appears? Probably not. If I get rid of every compiler warning when it first crops up, my code stays cleaner and new warnings are instantly detected.

In this case, I can only eradicate the warning by removing the call to -uniqueIdentifier, which obviously I don’t want to do. So the next best option is to suppress the warning; it’s a known issue, so I don’t want that warning message cluttering up my build logs for the reasons already mentioned.

The specific warning I want to suppress is deprecated-declarations, as you can see in clang’s error output above. (That [-Wdeprecated-declarations] indicates that I’m seeing the error because I’ve got the deprecated-declarations warning enabled. As an aside, those notes identifying the warning class that’s generating a warning are just one of the reasons I love clang; gcc was never that helpful.)

I could set Xcode to ignore that error on a per-file basis by setting -Wno-deprecated-declarations as a compiler flag for the file in question. (To do that, select your target in Xcode, click on the Build Phases tab, expand the Compile Sources section, then double-click the file you want to add a compiler flag to). But in this case I really want to ignore the warning just for that one line. If any other code in this file uses a deprecated declaration, I want to know about it.

It turns out the way to do it is with clang pragmas. Here’s how:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifdef TESTING
    /*
     Disable deprecated-declarations warning.
     See http://clang.llvm.org/docs/UsersManual.html#diagnostics_pragmas
     
     Basic workflow:
     
        1. push current warnings onto stack
        2. ignore warning we know will get thrown
        3. do dodgy thing that causes warning
        4. pop warnings - go back to what we had before we started fiddling with them
     
     */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
#pragma clang diagnostic pop
#endif

Job done.

On Zippity – Notes From a Small iPhone App

Zippity hit the App store last week. It’s a simple utility app for opening zip files, and I’m very proud of it. That’s no mistake; from day one I worked hard to make sure Zippity would be an app I was proud of. Here are a few of the decisions I made along the way.

Zippity uses standard UI elements

To some, standard UI elements might seem like the lazy option, but for me there’s always a strong urge to be creative and create some fun little widgets to make an app stand out. It’s a truth universally acknowledged that every iOS developer secretly wants to work for Tapbots. :-)

For a utility app like Zippity though, I didn’t feel there was any room for unorthodox UI elements. This isn’t an app that people will live in day in, day out. It’s an app that most people will only use occasionally, and for those users it’s going to be a pain if every time they open the app they have to re-learn how to use it. So, my first rule was: no custom UI elements where there’s a UIKit alternative.

On a similar note, I also didn’t want functionality that could only be accessed by gestures. Gestures offer developers a nice way to add “power user” features, but Zippity is built from the ground up to make all of its functionality obvious to casual users.

Zippity doesn’t want to be your friend

Zippity won’t ask you to like it on Facebook, rate it on the App Store or recommend it to your friends. I don’t like apps that nag me for my approval (even though I understand why they do it), so Zippity doesn’t do that. This might mean I die a penniless failure while other app developers enjoy the rich rewards of tapping into the social graph, but I’m fine with that. At least I’ll never be remembered as the guy who wrote that needy zip file app.

Zippity makes sensible choices about what to show you and how to show it

By default, Zippity doesn’t show file extensions (although you can change that if you really want to). Instead, the icon next to a file generally shows what sort of file it is. (For visually impaired users however, Zippity does speak the file extension, since those icons are only useful clues for sighted users.)

When showing you the contents of a zip file with multiple nested, otherwise-empty folders, Zippity collapses those folders down. For example, if the physical structure of the unzipped contents looks like this:

Illustration of Zippity's physical folder structure

Zippity will present a logical view that looks like this:

Illustration of Zippity's logical folder structure

Zippity talks your language

I’ve tried to avoid technical language wherever possible in Zippity’s interface and associated user-facing content. For example in its App Store description, on the Zippity website and anywhere else you see blurb about Zippity it talks about being an app for opening “zip files”. In fact Zippity handles a number of other archive formats, more geeky stuff like .tar, .gz and .bzip2, but I use the term “zip files” to cover all these archive formats. While that’s technically incorrect, it’s the terminology that makes sense to most people.

Zippity talks your language in another way too; if you’re a Dutch or Hungarian speaker then Zippity 1.0.1 (currently awaiting App Store approval) has an interface completely translated into your mother tongue. Other translations are coming soon! (If you’re interested in helping to translate Zippity into your language please get in touch.)

Zippity looks nice

Even utility apps can look nice, and Zippity’s no exception. It has a great icon (and another great, bespoke document icon) created by my friend and iconic iconographer, Jon Hicks. A different navigation bar colour and a subtle background from subtlepatterns.com in the “About Zippity” view give it a distinct visual identity and help to ensure that it can’t be mistaken at a glance for Zippity’s main view (i.e. the contents of a zip file).

The Hierarchy in iOS 5’s Photo Library

I finally had a need to get my head around the complex hierarchy of photos in iOS’s Photos app, and how they mapped to the flatter hierarchy in the iOS photo picker control. Here are the fruits of my labour – hope you find them helpful. (Click the image for a large version.)

Photo organisation in iOS 5.0

Running a Simple Git Server Using SSH

I had the need recently to set up a temporary git remote on a Mac mini on my local network. It turned out to be both easy and useful, so I thought I’d document how I did it.

Throughout this article I’ll refer to the Mac mini where I set up the remote repository as the server, and my Macbook Pro (my primary work computer) as my laptop.

What I wanted

I use Github a lot, so I’m used to representing my remotes in the SSH style, like this:

git@github.com:simonwhitaker/PyAPNs.git

Then I’ll clone that remote by running:

git clone git@github.com:simonwhitaker/PyAPNs.git

and away I go. So, the question was: how do I create a remote on my own device and access it in the same way?

Unpicking the SSH notation

It’s helpful to first understand what the SSH notation actually means. It’s really simple. The basic format is:

[username]@[hostname]:[path to repository]

That’s it. So that Github connect string from earlier means that I’m connecting to the server at github.com as the git user, and cloning the repository that exists at simonwhitaker/PyAPNs.git within the git user’s home directory.

Hang on though – I don’t have a password for the git user on github.com, so how come I can read and write stuff in their home directory? It’s because I’ve set up passwordless SSH by uploading my SSH public key to Github. When I connect via SSH, the SSH server at github.com looks to see if the private SSH key on my computer matches a public key on github.com. If it does, I’m allowed in.

So, here’s what I had to do to set up a git server on my Mac mini:

  1. Create a new user called git
  2. Set up passwordless SSH so that I can connect to my git user’s account without a password
  3. Create a directory in git’s home directory for storing my repositories
  4. For each repository I want to host: log in to git’s account on my Mac mini and initialise a new Git repository.

Step 1: Create the git user

The steps on how to do this will vary depending on your operating system. The git user doesn’t need (indeed, shouldn’t have) administrator or sudo privileges; a plain old user account will do fine.

For the sake of simplicity, on the server (running OS X, remember) I just opened System Preferences > Accounts and added a new user, setting their username to git and giving them a suitably strong password.

(That’s not a perfect solution: it sets git up as a regular user so e.g. they appear in the login screen, but it works fine for me. If you’re using OS X and you want to create a user who doesn’t appear in the login screen, see this Super User answer for details.)

Step 2: Set up passwordless SSH

On my laptop I opened a terminal window and checked to see if I had a current public SSH key:

$ ls ~/.ssh/id_rsa.pub

I did. (If you don’t have one, you can create a new public/private key pair by running ssh-keygen and following the prompts.)

Next I copied my public key to the git user’s home directory on the server, like this:

$ scp ~/.ssh/id_rsa.pub git@Goo-mini.local:

When prompted, I entered git’s password (the one I set up earlier). Then I connected to the server over SSH, again using the git account:

$ ssh git@Goo-mini.local

Again, I entered the git user’s password when prompted.

Once logged in as git, I copied my public SSH key into the list of authorised keys for that user.

$ mkdir -p .ssh
$ cat id_rsa.pub >> .ssh/authorized_keys

Then I locked down the permissions on .ssh and its contents, since the SSH server is fussy about this.

$ chmod 700 .ssh
$ chmod 400 .ssh/authorized_keys

That’s it, passwordless-SSH is now set up. If you’re following along at home you can try it for yourself: log out of the server then log back in:

$ ssh git@[your server name]

You should be logged straight in without being prompted for a password. If you’re not, something’s gone wrong – check through the instructions so far and make sure you didn’t miss something.

Step 3: Create a directory in git’s home directory for storing my repositories

Logging in to the server once again as git, I created a directory called simon in git’s home directory.

$ ssh git@Goo-mini.local
$ mkdir simon

Simple.

Step 4: For each repository I want to host, log in to git’s account on my Mac mini and initialise a new Git repository.

This one was pretty simple, too. First I created a directory to hold the repository. I followed Github’s convention and named the directory as [project name].git:

$ ssh git@Goo-mini.local
$ mkdir simon/myproject.git

Finally, I initialised a bare Git repository in that directory:

$ cd simon/myproject.git
$ git init --bare

Note the --bare option to git init: that means that rather than creating all Git’s config in a .git subdirectory it’ll be created directly in myproject.git, which is what we want.

Trying it out

All that was left was to push my repository to my new git server. On my laptop I ran the following:

$ cd /path/to/git/repo
$ git remote add origin git@Goo-mini.local:simon/myproject.git
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 232 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@Goo-mini.local:simon/myproject.git
 * [new branch]      master -> master

Cooooool!

Next steps

There are loads of ways you could improve this process. For example, any user who uploads their public SSH key would have access to all repositories in git’s home directory. You might want to change that, for example so that users can only write to their own repositories.

If you find ways to improve the process, please do let me know.

Dated PDF Saver

Update (29/2/12): Script tweaked to make sure that it generates a unique target filename rather than overwriting existing contents in the target directory. (See updated code below. If you’ve got the version with the unique_path function you’re good to go.)

Here’s a nifty script I use to save emailed receipts as PDFs in a folder hierarchy that includes the date (year and month) on which they were saved. It helps keep my business receipts organised and saves me a bit of time when I’m looking for receipts after the event. Hopefully you might find it useful too. It’s on Github so feel free to clone, tweak and share.

(Don’t ask about the license. It’s a 2-line shell script with 65 lines of comments and context. It’s free, in every possible sense of the word.)

save-dated-pdf.sh View Gist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/bin/bash
#
# save-dated-pdf.sh
# 
# Save PDFs from the OS X PDF menu to a folder
# named according to the current date, of the
# form /path/YYYY/MM/
# 
# For example, you could use this to export receipts
# you receive via email during February 2012 to 
# ~/Documents/Receipts/2012/02/some-receipt.pdf
# 
# USAGE
# 
#   save-dated-pdf.sh title options inputfile
# 
# (But see installation below for using as a PDF
# workflow.)
# 
# INSTALLATION
# 
# Put save-dated-pdf.sh in the folder where you
# want the date-specific folders rooted. In the above
# example this would be ~/Documents/Receipts/
# 
# Create an alias to the shell script in 
# ~/Library/PDF Services, and name it accordingly
# e.g. "Save PDF to this month's receipts.sh".
# (Note: that's an alias, not a symlink. So e.g.
# drag it in the Finder while holding down CMD+ALT.
# A symlink might work too, but I haven't tried it.)
# 
# Now when you have something you want to save as a 
# PDF in that folder, just print it (File > Print from 
# the menu, or CMD+P) and choose your new entry from
# the PDF dropdown.
# 
# SEE ALSO
# 
# http://bit.ly/w0g4zc for documentation on using
# shell scripts as PDF workflow options

unique_path()
{
    # Given an absolute file path as input, 
    # generates a unique file path by appending
    # an incrementing integer to the end of the
    # filename portion. 
    # 
    # e.g. if input is /path/to/foo.txt and that
    # path already exists, will return
    # /path/to/foo-1.txt. If foo.txt and foo-1.txt
    # exist, returns /path/to/foo-2.txt, and so on.

    if [ -z "$1" ]; then
        echo "unique_path requires an argument"
        return 1
    fi
    
    path="$1"
    directory=$(dirname "$path")
    filename_with_ext=$(basename "$path")
    extension=${filename_with_ext##*.}
    filename=${filename_with_ext%.*}

    suffix=1

    while [ -e "$path" ]; do
       path="${directory}/${filename}-${suffix}.${extension}"
       suffix=$(( $suffix + 1 ))
    done

    echo "${path}"
    return 0
}

pdf_title=$1
pdf_input_file=$3
current_year=$(date "+%Y")
current_month=$(date "+%m")

target_basedir=$(dirname "$0")
target_dir="${target_basedir}/${current_year}/${current_month}/"
target_path=$(unique_path "${target_dir}/${pdf_title}.pdf")
target_filename=$(basename "$target_path")

growl_notify="/usr/local/bin/growlnotify"
growl_app_name="Dated PDF Saver"

mkdir -p "$target_dir"

mv "$pdf_input_file" "$target_path"

# If the move succeeded and we've got growlnotify installed,
# send a Growl notification
if [ $? -a -x $growl_notify ]; then
     $growl_notify \
        -n "$growl_app_name" \
        -a Preview \
        -m "File was saved to $target_dir" "Saved $target_filename"
fi

The Symbolicator Helps Those Who Help Themselves

It sounds like I’m not the only one having problems with Xcode 4 not symbolicating crash logs correctly. Here’s the symptom: I drag crash logs that testers email me into Xcode 4’s organiser, then sit and wait for symbolication to complete. But once it’s done, my logs aren’t symbolicated – they still just show a load of memory locations.

Running the symbolicator at the command line sheds a bit of light on what’s going wrong:

$ /Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks\
> /DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash MyApp_etc.crash

Here’s the output I get:

Can't understand the output from otool ( -> '\/Developer\/Platforms\/
iPhoneOS\.platform\/Developer\/usr\/bin\/otool -arch armv7 -l   
/Users/simon/Library/Developer/Xcode/DerivedData/MyApp-fbxifqioardhgxaitjdftgoejjzz/
Build/Products/Debug-iphonesimulator/MyApp.app/MyApp') at 
/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/
DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash line 323.

Hmm… Notice that path to the app?

~/Library/Developer/Xcode/​DerivedData/MyApp-fbxifqioardhgxaitjdftgoejjzz/​Build/Products/Debug-iphonesimulator/​MyApp.app/MyApp

That’s not the right app file – it’s the build output from a Debug build, not an AdHoc build, and worse it’s a Debug build for the iOS simulator.

As you may know, the symbolicator uses Spotlight to find the .app file (and the .dSYM file) it uses to symbolicate a log. And that means that there’s a really simple fix. Adding ~/Library/Developer/Xcode/DerivedData/ to the list of directories that Spotlight doesn’t index makes those build artefacts invisible to Spotlight, and hence the symbolicator. Just open System Preferences, click on Spotlight, switch to the Privacy tab and add that DerivedData folder to the list.

You may now find that the symbolicator has a similar problem with apps installed on the iPhone simulator itself. Adding ~/Library/Application Support/iPhone Simulator/ to Spotlight’s ignore list nails that one.

Now when I run the symbolicator at the command line, I get properly symbolicated output, just as expected.

UPDATE: See the comments on this Stack Overflow answer of mine. This solution is not without its gotchas; it can interfere with correct running of the Instruments app.

Debugging in Analogue

One of the most interesting challenges of our recently-launched app for The xx was getting the video synchronisation as tight as possible across multiple devices. The app uses a bit of clever logic to determine, to the best of its ability, the clock offsets between the various devices. The device that’s playing server then uses that to send a trigger signal to all the devices (including itself), saying “start playing at exactly time t” - where t is adjusted for each device based on its clock offset so that all devices get the same relative trigger time.

For example, imagine two devices, D1 and D2. D2’s clock is 2 seconds ahead of D1’s clock – so when D1 thinks it’s 12:00:00, D2 thinks it’s 12:00:02. Once the devices are aware of this discrepancy they can compensate for it: D1 might tell itself “start playing at 13:30:00” while telling D2 “start playing at 13:30:02”. The effect is that both devices start playing at the same time.

At least, that’s the theory. In practice, iOS is a non-deterministic, multitasking operating system, and it’s quite possible that at the alloted time it’ll be busy refreshing the UI, checking for mail, or doing any number of other things. By the time the thread that’s going to hit play on the video gets serviced the devices might have slipped significantly out of sync.

There’s no way around this, and occasionally the syncing isn’t perfect (especially on older iPhone and iPod models), but you can mitigate the effect by having each device spin up a new, high-priority thread when they receive the play signal, and have this high-priority thread start the video at the alloted time.

In iOS there are a number of ways of starting new threads, including NSThread, NSOperationQueue and Grand Central Dispatch. I was curious to know which of these methods yielded the most favourable results for our app. So, I set up a test scenario with a difference.

First, using Amadeus Pro I created a WAV file containing a single, 100Hz square wave. Then I inserted this sound at the start of the soundtrack of a sample video. I loaded a test app that played the sample video onto a pair of iPod Touch 3Gs and using some audio splitters and cables, connected the iPods to the line-in on my MacBook Pro such that one iPod contributed the left channel and the other the right. It looked something like this:

Then, once again in Amadeus Pro, I hit record, then hit play in my test app. Once the square wave had played I could stop recording, zoom in on the audio track I’d just recorded, and look at the distance between the falling edge of the square wave on the two channels to see the time lag between the two videos:

I repeated the experiment a number of times with each threading approach and compared the figures to make sure I was choosing the best option. Average time delay per method was as follows:

MethodAverage time delay (ms)
Schedule play on the main thread114
NSOperationQueue7
Grand Central Dispatch9
NSThread10

So, perhaps as expected, there’s not much to choose between the three threading architectures I tried, although they all give performance that’s an order of magnitude better than the performance you get if you schedule the play signal on the main thread.

Build and They Might Come

We’re making iPhone software primarily for three reasons

Great article from Marco Arment about why just building a new hardware platform won’t in and of itself cause developers to flock to it.