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!
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:
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:
Toggle replies bookmarklet
This bookmarklet toggles replies on and off when viewing someone’s timeline at twitter.com.
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.
Instructions: drag the link to your bookmarks bar, click to invoke.
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.
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
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
(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]];
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
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:
Zippity will present a logical view that looks like this:
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).
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:
Then I’ll clone that remote by running:
git clone firstname.lastname@example.org: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:
- Create a new user called git
- Set up passwordless SSH so that I can connect to my git user’s account without a password
- Create a directory in git’s home directory for storing my repositories
- 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
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
--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
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.
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.)
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
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?
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.
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:
|Method||Average time delay (ms)|
|Schedule play on the main thread||114|
|Grand Central Dispatch||9|
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.
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.