First a little back story.

The other day my mom called me up saying that her 11" MacBook Air was out of space and running slow wondering what we could do about it. Knowing that OS X runs pretty terribly without some free space I figured I'd run some of the Onyx cleanup scripts, check out where the space was being used up with Daisy Disk and get rid of some of the crap.

Onyx cleared a few gigs, and Daisy Disk said that iPhoto was the main culprit — the library being nearly 80GB,  67% of the total space on the disk.

Firing up iPhoto I created a Smart Album to see how much space videos were taking up.

The Smart Album conditions

Turns out there was nearly 10GB in videos, so I selected all of the items in the Movies album and exported them to an external disk. After removing these from the library, emptying the iPhoto trash and the actual Trash the disk had about 15GB free.

Success!

Not so fast

Of course not. After handing the laptop back my mom complained that iPhoto was running really sluggish. I got it back, checked it out and it was unusable. Scrolling had a 5-6 second lag and the UI responsiveness was generally pretty terrible.

My guess is that removing those videos screwed up the library somehow, so I opened up iPhoto holding down CMD+OPT to get the repair menu up and ran each item on the list (repairing permissions, rebuilding thumbnails, reindexing photos, repairing the library). Many, many hours later everything was still terrible so I decided to start from scratch.

My mom's iPhoto library is fairly large — nearly 15,000 photos. Initially I thought her 11" may not be able to handle that many photos so I figured exporting all of the photos and only importing the ones from the last two years would be a way of seeing if that was the case.

I left the laptop overnight exporting the 15,000 photos, backed up the library and deleted the original. Now I just had to find the photos taken within the last two years.

What should be easy...

Once again foiled. All of the exported photos had date of creation and modification set to yesterday. The sub-folders were all album names and not really indicative of when the photos were taken. I peeked inside the iPhoto library package contents to the Original and Modified folders where the photos live but found duplicates, terrible names and general cruft so I figured the actual exported photos had to be salvaged somehow.

Then I remembered EXIF data. Every photo you take contains metadata about what camera was used to take it, the model, ISO, exposure time, etc. One of those fields is the date it was taken. So if I could figure out how to read the date taken I could write a script to "fix" the photos.

ExifTool to the rescue!

With ExifTool in hand I went off to Automator and wrote some Ruby that would loop through a set of folders and change the date modified to the date the photo was taken as dictated by its EXIF data. It looked like so (download it here):

I tested this on a folder and it worked! I could follow along what was going on in Console.app and all was good.

Then I ran it on the main folder and encountered an interesting bug:

Run Shell Script failed - too many arguments.

Automator was passing in the path names to all of the photos to Ruby as arguments, but someone could only handle 4096 of these. Well below the 15,000 items I wanted to process.

Having spent enough time on this already I decided to be lazy and just run it a few times. Due to the logging to the Console I could see how far it had got and simply resume it from there with a next batch. There's also a bug where photos with single quotes or ampersands had to be renamed to be processed, which I could have fixed but again, lazy.

Now only the modified date was supposed to be changed, but I guess OS X doesn't allow a creation date to be later than its modified date so they both changed as a bonus.

Now for some Spotlight magic...

Now that the photos finally had their dates back I could make a simple search for files that were JPEGs and created in the last two years.

Importing these into a new iPhoto library took a while but once it was done scrolling was fluid and the app was generally responsive. Great! So I backed up this library and decided to try importing the rest of the photos.

A few hours later all of the photos were back in iPhoto and the app was still scrolling smoothly. What the fuck?

My guess is that removing the videos screwed up the original library in a way that iPhoto itself couldn't fix. Maybe deleting the video files themselves from the disk would have been better — leaving orphans in the iPhoto library. I have no idea.

In any case it was a learning experience and yet again proof that Automator is truly a hidden gem that I don't use enough.

You can download the Automator app (open it in Automator to edit it if you like), but remember you must have ExifTool installed first.

Posted
AuthorPaulo Fierro

I've been shooting some video with the GoPro Hero 3 Black which can do 1080p at 60fps which looks lovely.

In VLC.

Yup, in OS X 10.8.2 playing back 1080p video at 60fps on my MacBook Pro results in so much stuttering its unusable. Trying to edit it in Final Cut Pro X is basically impossible or will drive you crazy in the process.

You get lovely warning messages like this:

Thanks, Final Cut!

Thanks, Final Cut!

As VLC handles it fine I know my laptop has more than enough fire power to play it back, so it must be a QuickTime issue (Final Cut uses QuickTime to decode video as far as I know). According to multiple discussions on the Apple support forums its possibly related to a GPU driver issue and will hopefully be resolved in 10.8.3.

I hope so...

Posted
AuthorPaulo Fierro

At the end of November last year we had our good friend Chris Ross come over and visit us. His visit had a caveat, he had to do the PADI e-Learning course online first so we could go diving while he was here.

On the flip side, we would learn how to kitesurf — I for one had no idea you could actually do that on the island... So we took a few lessons and kind of got there but not really. Turns out learning to dive is much, much, much easier.

In any case, I've now had five lessons and found that was enough to go out and try some riding on my own, slowly building up confidence and lets call it technique.

This first video is the drive to Barkers Beach, sped up 20x, shot with a GoPro Hero 3 Black attached to the windshield with a suction-cup mount.

Next up is some shots taken while riding using CamRig's Universal Strut Mount and a standard board mount.

As its the "windy season" I look forward to getting better, and maybe even learning how to turn without stopping.

Posted
AuthorPaulo Fierro

A late afternoon dive off Coconut Villas while Mark was visiting, led as always by Stephen.

Posted
AuthorPaulo Fierro

For my birthday Niqui got me this dive mask from Liquid Image. Its a nice mask and the video taken with it is great but the four rechargeable AAA batteries power it for about 90 minutes. Most of our dives are an hour or less so that's not a problem, and the mask comes with a battery recharger – however while charging the batteries the camera forgets the date.

You have to set the date each and every time. Its a minor thing but it annoys me so I don't do it. For some reason the default date is set to Monday, January 8, 2035 – a strange choice.

So because I'm lazy and don't change the date on the camera every time, when I eventually drag the video files into Final Cut I get crap like this:

Transient

Having some events with the right date and some in the future makes organising video for projects a nightmare. So after googling around I found that I could use the touch command in the Terminal to change the creation date. If I do this on each file before importing it into Final Cut I avoid the problem. So the command is basically:

touch -t yyyymmddHHMM <PATH_TO_FILE>

Still, doing this on multiple files is a massive pain so I toyed with the idea of writing a script for it. I have used Automator in the past and figured doing things like selecting items using the Finder's File Selection dialog is easier than using a command line script so that's what I did and you can see the "recipe" below.

It accepts a string which is the date you want the file to have been created, you select the files and you're done. It keeps the original time too, which may not be something you want but I don't really care about the time. As long as the files have the right date and are sequenced correctly its good enough for me.

You can download the application here. You can also open it up with Automator and modify it as you see fit.

While doing this I learned Automator actually has support for variables – who knew? Its a powerful tool and I don't think I use it enough.

Transient
Posted
AuthorPaulo Fierro

So iOS6 is now officially released so we can talk about it. We've been using it since WWDC in June and in general there's so much to like in this release. The one thing that is still lacking is mapping data.

Having used the beta while we were in California I know first hand how great the maps can be. Turn by turn navigation when driving from San Francisco to Las Vegas worked well and the maps were pretty detailed showing local businesses, restaurants, etc. Once you leave the US however, the mapping data is substantially worse, with the exception of a few 3D flyovers.

There's been a lot of noise on Twitter and people holding out on buying a new phone or upgrading until this is fixed. The good news is that this is all on the backend so Apple can roll out improvements behind the scenes and most likely not require us to do anything.

Most of the complaints I've seen however still have some data visible to them. Maybe not all that accurate, but something.

However here on Grand Cayman the Maps app has gone from useful to completely useless. On the left is a screenshot of iOS5 that has roads and place marks – directions work too. On the right is iOS6 that shows a place mark for the airport and otherwise a lot of sand. Directions do not work.

They replaced our roads with sand. Thankfully we got to keep the airport.

The satellite view isn't as bad, but quite cloudy and the more you zoom out the more the clouds appear.

Satellite view

There is also something weird that happens to the shape of the island around the middle right.

Strange shape morph

This appears to be because they draw the outline based on existing data. As you get closer you get to that location in satellite view you can see a bad stitch job between what I assume are two different data providers. The image on the right is basically black and white.

Weird stitch job

In any case, I look forward to the day when the maps here are as gorgeous as they are in San Francisco. No doubt the Maps team in Cupertino are quite busy and we're not very high on the list of priorities but hopefully we'll see something soon.

Update: Haha! My buddy Peeks just let me know that the image I submitted to "The Amazing iOS6 Maps" Tumblr made it onto Wired.com :)

Update #2: And TechnoBuffalo. And CNET. And Jest. And Broadband Reports. And Droid Life. And Huffington Post.

Update #3 (June 24, 2013): I just had a look and to my surprise we now have a road!

ios6Road.jpg
Posted
AuthorPaulo Fierro

Back in 2003-2004 when I was still studying in Oslo I decided to write my own blogging "platform" as a way of getting of practicing my then-newfound Perl hacking skills. I wrote the admin interface in Flash, they talked to each other using this library called AMF::Perl which, though awesome, never made it passed version 0.15.

Fast forward to a week and a half ago when my host media temple upgraded the PHP install. Unannounced and unbeknownst to me they also upgraded their Perl installation which broke my site. The code is 8 years old, I'm not surprised - if you're interested it was the library that talks to the database that does something which has since been deprecated. I haven't written any Perl in at least 5 years so I'm not sure how to fix it. My interest in digging down and fixing a third party library is zero.

So after hearing so much about Squarespace on nearly every podcast I listen to I decided to give it a shot and here we are. Hopefully I'll be able to transfer over the old posts (at least some of them) but we'll see.

In any case, so far Squarespace seems great. And the best part is I no longer have to maintain it.

Win.

Posted
AuthorPaulo Fierro

So we are in San Francisco for WWDC and want to avoid roaming costs. Our inital plan of using HolidayPhone failed as they forgot to add credit to our SIMs.

After much back and forth I finally got data working on our AT&T GoPhone (Pay As You Go plan). The people at the store assured us this would not work and is not possible, but they're wrong. We paid $10 for the $2 per day unlimited plan SIM. Unfortunately this plan no longer supports a data add-on so you have to upgrade it. There's a $25 per month plan with minutes and texts and a $50 per month one with unlimited everything. I don't care about the minutes or texts, but you may want to compare the two.

  1. To upgrade it, first you have to add money to your account. You can do this in store or online, I went to myprepaidrefill.com.
  2. Click on Login
  3. You probably don't have a password, so click on the forgotten password link for Pay As You Go
  4. Enter in your phone number and you'll get a text with the password. Now login.
  5. Click on Add Features, select Refill and add $25. You can look at the Data Packages section but you'll see a warning triangle. Hovering over it will tell you that a data plan is not supported on the Unlimited $2 per day plan.
  6. Once you've added money to the account, log out. Unfortunately you can't upgrade your plan on the site (or I couldn't find it) but you can go through an automated voice system by calling 1-800-901-9878. You want to upgrade to the $25 plan and this happens instantly. Trying to do so without enough money in the account doesn't work which is why we added funds first.
  7. Once that's done, log back in and you should see that your plan's changed. Now you can go back to Add Features, select Data Packages and select one. I went with $5 for 50MB just to test that this works.
  8. On your iPhone, hop on a wifi network visit unlockit.co.nz, go to Custom APN and select United States and "AT&T PAYG". Don't select plan "AT&T" - it didn't work for me. Then click "Create Profile" and it will install itself on the phone.

Now disconnect from the wifi and hopefull you'll have data working. It does cost a little bit ($10 for the SIM, $25 for the upgrade and then $5 or more for data) but its worth it.

Note: On the AT&T site you'll notice oddities like not being able to paste in your credit card number, you can set the State to "Foreign" when entering in your address but the Country can't be changed from "United States". It doesn't support non-US postal codes either so in my case I had to enter "BN13W" instead of "BN1 3WB" but it still works.

If you want to avoid the hassle you can go with T-Mobile, but then you're stuck with Edge unless you're near the Moscone center where they are apparently testing out 4G.

Posted
AuthorPaulo Fierro

Update (July 30, 2012):

If you've upgraded to Mountain Lion the below instructions still work. Once complete you should check out this guide. It contains a tip on installing gcc via brew in order to avoid using CLANG which can result in occasional errors when installing/updating gems.

--- End of update ---

A few weeks back I decided to take the plunge and wipe my machine and set it up from scratch. The main reason being I was running out of space on my SSD and I just wanted to see what a fresh Lion install would be like.

Let me tell you, its FAST! Totally worth it. It did take me some time to find the right resources and shave some yaks so this is more of a guide for future-me to save some time. If you do carry on reading from this point I assume you know what you're doing :)

So now that everything is wiped and fresh and new (backed up of course) the main task is setting up ye old dev environment. Lets start at the beginning.

Xcode

Head over to the Mac App Store and download Xcode. Once you install it you'll find that it no longer lives in /Developer. Its been moved to /Applications and its command line tools are no longer in the $PATH on the command line.

We don't have to add them ourselves, simply start up Xcode, go to Preferences and then Downloads > Components. Click on the "Install" button next to "Command Line Tools".

Heroku

If you're deploying stuff on Heroku, grab the toolbelt here.

Homebrew

Previously I was using MacPorts, but after hearing so many great things about Homebrew I took the plunge. Its simply a great package manager and you can download it here. For example, OS X doesn't ship with wget, so to install it you can simply run the following on the command line:

brew install wget

Done. Moving on.

RVM

RVM is a command-line tool which allows you to easily install, manage, and work with multiple ruby environments from interpreters to sets of gems.

What it says on the tin. It allows you to easily install multiple versions of Ruby on your system. It also allows you create gem sets and swap between them. To install:

bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

Then:

source ~/.rvm/scripts/rvm

Now we're ready to install Ruby! The only problem is that with Xcode 4 the provided compiler (gcc) is LLVM based and not yet fully supported by Ruby and gems. Now before you go hunting for an older gcc (which I did) its not necessary.

To install Ruby 1.9.2 (which is required for Heroku) enter:

rvm install 1.9.2 --with-gcc=clang

When that is done, lets check what version of Ruby we have set as the system default:

ruby --version

Mine said Ruby 1.8.7. Lets set 1.9.2 to be default:

rvm use 1.9.2 --default

If you run ruby --version it should now say 1.9.2. If you ever want to go back to the system default simply enter rvm system. So now we have an easy way of swapping Ruby versions and you can read more about it here.

Sublime Text

Sublime Text is my favourite editor of all time. Get it here. Read some great tips and tricks here.

MySQL

Previously I was using MAMP. However I had an issue installing the do_mysql gem which is required for dm-mysql-adapter I was using with Datamapper.

Long story short I found the easiest thing to do was install mysql via brew instead of using MAMP. So to start off:

brew install mysql

Next to configure the installation (all one line):

mysql_install_db --verbose --basedir="$(brew --prefix mysql)" --datadir=/usr/local/var/mysql --tmpdir=/tmp

To get it to run on startup, first we have to create the directory if it doesn't exist:

mkdir -p ~/Library/LaunchAgents

Then we copy the mysql.plist over (the version number may have changed when you're reading this):

cp /usr/local/Cellar/mysql/5.5.20/homebrew.mxcl.mysql.plist ~/Library/LaunchAgents/

Then we tell launchctl to load it on startup:

launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

Now to set the password:

mysqladmin -u root password {new-password}

Phew! Ok, now mysql is installed and running on port 3306. To create your first database simply enter:

mysqladmin -u root -p create {your-database-name}

And some gem love...

Finally I recommend using bundler for gem management, foreman to run your app (especially if you're on Heroku), rerun to rerun your app when something changes, datamapper for ORM and sinatra for happiness.

Good luck!

Posted
AuthorPaulo Fierro

I just bought Reflection about 20 minutes ago and its awesome. It allows you to AirPlay mirror your iPhone 4S or iPad 2 to any Mac running Snow Leopard or later over WiFi. This is fantastic because in the past we had three options:

  1. Use the Simulator, but there are certain features (gestures, accelerometer, etc) that are hard or impossible to demo. Yes there may be tools to help us with some of them, but its a pain
  2. Set up some sort of camera rig to film what it is we're demoing. The nicest one I've seen is Heiko Behrens' CamHolder. I saw one of his earlier prototypes at Beyond Tellerand last year and its a great idea, but it means extra stuff to carry.
  3. Bring an Apple TV with you, hope the projector supports HDMI or buy an HDMI to DVI/VGA converter and AirPlay mirror to that. But then there's the issue of swapping displays, the time lag while swapping and hoping the projector is up to task. And you have to bring it with you.

Those days are long gone! This is what I will be doing in the future:

Step 1: Set up Reflection

Download the app, install and run it. Now ensure your Mac and iOS device are on the same WiFi network. If there is no WiFi available, create a wireless network on your Mac or share the wired connection. Now connect your device to this newly created network.

airplay-1.jpg
airplay-2.jpg

On your iOS device, double tap the home button, scroll all the way to the left and tap the AirPlay button. Select your Mac from the list of available options and select the Mirroring option.

You should now see a mirror image of the iOS device on your Mac. Finally enter full screen mode (Device > Enter Full Screen) or press CMD+F. Reflection will go into its own full screen space thing.

Step 2: Set up Keynote

Open up Keynote and any presentation. Go to Preferences > Slideshow and enable "Allow Expose, Dashboard and others to use screen".

Step 3: Awesome

Now for the fun part. Enter full screen mode in Keynote and then hit Play to start the presentation. Three finger horizontal swipe on your trackpad to swap to the Reflection app, showing your iOS device in full screen. Three finger swipe back to go back to your presentation.

How freaking cool is that? No swapping displays, no lag, no lugging extra kit with you, just ultra-smooth swipy goodness.

Yeah, I like this a lot.

PS: I have only tested this on Mountain Lion. I assume it works on Lion, no idea about Snow Leopard.

Update: Mike confirms it works on Snow Leopard in the comments. Wicked!

Posted
AuthorPaulo Fierro

This time last week marked the end of Flash on the Beach as we know it. The end of what I have always considered the best fucking conference I've ever attended. John Davey who organises it, always pours his heart and soul into it along with his wife and team of volunteers and it shows. FOTB has a special place in my heart for various reasons.

I remember sitting at my desk, working at a startup in Norway talking to some friends over IM who had just come back from the first one in 2006 ranting and raving about how amazing it had been. I decided then and there that I was going to the one next year even if it meant taking some vacation time because this was obviously something I had to experience for myself.

So in 2007 I flew over in November and stayed at a very simple bed and breakfast in Kemptown where I could marvel at the rainbow flags. Thanks to my good friend Chris Pelsor I managed to meet pretty much everybody who I had followed through the years, via their blogs and work. These were "rockstars", people who's work amazed me and yet, at the end of the day were just people. People like you and I, most of them awesome, some of them extremely shy. I think that was my biggest take away from the conference and something FOTB gets right every time. There is so much mingling between attendees and speakers that it sort of becomes this one collective mass of people who enjoy making cool shit. How can that not be fun.

Most importantly though had it not been for FOTB I would've probably never have come over to Brighton and fallen in love with the city. I would have not come over the following years, gotten to know a certain someone better only to eventually fall head over heels in love and leave everything behind to move to this very city and set up shop here.

So for that reason alone I owe John and FOTB a huge amount of thanks. It may not have been the main reason but it certainly was a catalyst to changing my life. Damn.

But this year was different in one way. I was speaking.

When I was asked at the end of last year I said fuck yes, and then had several months of getting to grips with that idea. I haven't done an enormous amount of speaking - mostly user group sessions, a Flash Camp in Lisbon (which was ridiculous amounts of fun) and a remote session for a one-day conference in Ottawa. But FOTB was special and therefore the goal was simple - don't fuck it up.

The week before I would have been nervous but Niqui, Matt and I were still working on finishing this year's conference guide, a web app you can play with at fotb.me. How we built that is another blog post.

The only problem was my slot. I was speaking on Wednesday, right before lunch. This meant I couldn't really enjoy myself fully on Monday and Tuesday as I couldn't properly relax. No biggy, but hey. I was probably the only person who woke up Wednesday morning without a massive hangover so I guess I win :)

I feel that the session itself went well. I got some great feedback and was actually amazed at all the people that showed up. Being up against the JAM session, and Lee Brimelow from Adobe who stole the show at the Keynote I was expecting the room to be empty. Especially with the JAM session being described as:

"Rest assured, whatever we ask them, it will be hot! This is a session you need to put on your personal scheduler so that you don't miss it!"

Thanks John.

I did consider changing this and Lee's description to "Probably shit. Don't bother going."

Alas, the Pavilion Theatre (the little of the three venues) was actually half full. I was super impressed and if you were there, thank you :) There were several friends in the audience which only made it easier. The lighting guy made them all disappear too - in retrospect I think seeing people's faces might have been better. This was a smaller crowd than Lisbon for sure, but it was still FOTB. Don't fuck it up.

My favourite piece of feedback was from Neil Keith Young:

I then went into the Pavillion Theatre to see Paulo Fierro talk about his development of iPhone applications. His talk about developing and releasing an application through the app store was fantastic. He talked through his development of a Boris Bikes iPhone application for the Norwegian market and discussed the pit falls and also the excitement generated from building the application. Truly inspirational stuff and something that I would really love to look at, even if not from an Objective C application then from a html 5 website standpoint.

I owe this man a beer.

The guys at Ubelly also took some notes as well as Jens Brynildsen on Flash Magazine. He also took this pic where I have a pointier chin than Leno!

Anyways, enough about me. I wish I had gotten to see more sessions but I missed everything on Monday except the last two because I was in a state of panic going over my slides. I say going over, I mean recreating them because they original ones weren't good enough. Takes alot longer than you think.

My favourite sessions were Lee's bit of the keynote (hilarious), Jon Burgerman (who could happily leave painting for standup), Joel Gethin Lewis (who is actually helping people through technology. amazing), Remy Sharp (who got sung happy birthday twice! bet the W3C don't do that shit), Cyriak Harris (madman doing live AfterEffects'ing), Jared Ficklin (smoke machines mean instant win) and Josh Davis (who scares the shit out of me).

There are so many I would have loved to have seen but hopefully the videos will make it online sometime soon. Another highlight was the debauchery that happened at MyHotel. Less so than previous years, but still tons of fun. The Zacapa... mmm. The speaker discount didn't hurt either :)

Anyways, another year, another awesome FOTB. I'm really looking forward to what next year brings and hopefully once again see all of these wonderful friends who live in interesting places all over the world descend upon our little village once more, for whatever the new FOTB becomes.

Posted
AuthorPaulo Fierro

In a forthcoming blogpost I'll talk about not only the awesome Flash on the Beach conference that just flew by but also the web-app we built for it at fotb.me.

In one of the many bar chats, I was talking to Paul King about our experience building it and he asked if we'd played around with the accelerometer data. I said we had, albeit briefly and I incorrectly stated that we had gotten it working on iOS, Android and PlayBook.

iOS 4.2+ devices support the DeviceMotion API and so does the PlayBook. The only gotchas is that instead of listening for the devicemotion event you have to hook into the window.ondevicemotion function itself for this to work on the PlayBook.

For Android phones I quickly threw together a SWF that would simply listen to a flash.sensors.Accelerometer instance and then send those over ExternalInterface to a JavaScript handler which then takes it from there.

This is an extremely basic example, but all the code is on GitHub so get it there or check the demo. Since the PlayBook supports both Flash and devicemotion a simple check is done so we don't get the data twice.

It should go without saying that if your Android phone doesn't have Flash installed then you get nothing.

Posted
AuthorPaulo Fierro

When we were developing fotb.me we ensured the app would run offline using the fancy, new HTML5 app cache API. It can be a pain to create the cache manifest file by hand, this is obviously something that should be automated.

I looked around and found a plugin for Coda and a bundle for TextMate and TextWrangler which are good, but they don't allow me to ignore certain files and only add certain filetypes which were two things I needed. Not wanting to have to remember to edit the generated files manually I rolled our own.

CacheManifestGenerator is a simple Ruby script that traverses any folder recursively and makes a note of each file. You can add specific files to be ignored and the specific file extensions to look for. Very, very simple yet effective.

It also adds a comment with the time of creation. This is enough to trigger browsers to check the manifest contents as the file itself is different.

Oh and btw, Ruby is fun!

Update:

John Allsopp reminded me of manifestR, an online tool that creates a manifest for any page you are visiting via a bookmarklet. Very cool stuff, but it didn't work for us as the site has to be online, not be password protected (something we were avoiding pre-launch) and also the ignore files bit I mentioned above. However if you don't need those things then its definitely the easiest way of generating a manifest automatically.

Posted
AuthorPaulo Fierro

When creating web apps that are targeting iOS devices Apple provides a few hooks to allow things such as custom home-screen icons and splash screens for when people add them to their home screen. For icons you simply add the following to the <head> tag:

<link rel="apple-touch-icon" href="icon.png" />

If you want an icon without the default gloss you add -precomposed:

<link rel="apple-touch-icon-precomposed" href="icon.png" />

Different devices will need different sized images. The default of 57x57 is for older iOS devices, if you're targeting iPad you want 72x72 and if you're going to look good on the retina display on iPhone 4 you want 114x114. To tell the device which ones are present there is a sizes attribute where you can supply the size. So it looks like:

<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/images/icon-ipad.png" />
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/images/icon-iphone4.png" />
<link rel="apple-touch-icon-precomposed" href="/images/icon-iphone.png" />

Notice how the last, default one does not have a sizes attribute - it doesn't need it. However, we found that it has to go last otherwise it may get overlooked on iOS3 devices.

Retina display splash screens

So far so good right? Well not so fast. Sadly Apple has not provided the same level of flexibility for splash screens. The sizes property does not work and there is no official documentation on how to provide a high-quality image for retina displays.

Disappointing but I suppose its currently an unsupported feature. Bleeding edge, cuts, etc.

Using media queries

Googling around you'll find lots of people asking this question but none of the solutions I tried worked. Some suggested using the aforementioned sizes property. Others suggest using media queries to target the right device. This does work for iPad and you can target a splash screen for landscape or portrait as follows:

<link rel="apple-touch-startup-image" href="/images/splash-ipad-landscape.png"  
media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)" />
<link rel="apple-touch-startup-image" href="/images/splash-ipad-portrait.png" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)" />

The only strange thing is that the landscape splash image must be 748x1024 and you have to rotate the contents 90 degrees counter-clockwise. For portrait the dimensions are 768x1004.

So the logical thing would be to use the same sort of thing for targeting retina display. The regular splash image is 320x460 and the high-quality one is 640x920, so we were thinking we could target the right device using something like:

<link rel="apple-touch-startup-image" href="/images/splash-iphone.png" 
media="(max-device-width: 480px) and not (min-device-pixel-ratio: 2)" />
<link rel="apple-touch-startup-image" href="/images/splash-iphone4.png" media="(max-device-width: 480px) and (min-device-pixel-ratio: 2)" />

The first image is for devices that do not have a minimum device pixel ratio of 2. Sadly this does not work at all. So media queries do work for iPad detection and even iPad start orientation but not for iPhone 4. Frustrating.

PS: adding the -webkit prefix to min-device-pixel-ratio has no effect, I saw this a few places too.

So in an act of desperation we turned to JavaScript. We can check what type of device you are and if you have a retina display so we can try to inject an image for these devices. Select for spoiler alert -> this works.

JavaScript to the rescue

The logic is as follows:

  1. wait for when the document is ready
  2. check that the device is running iOS
  3. check that the device has a retina display
  4. check that the device is running iOS5 or above

The last point is important because an iPhone 4 running iOS4 does not support high-quality splash images (640x920) and will simply ignore any you provide. They don't display the low quality (320x460) one either, they just flat out refuse to display a splash image because you told it to do something it couldn't do. Its a bit touchy.

In any case we use jQuery to figure out when we have loaded, and general DOM magic allowing us to insert the high-resolution splash screen into the <head>, but only for iOS devices with a retina display running iOS5 or later.

function hasRetinaDisplay() {
	return (window.devicePixelRatio >= 2);
}
function isAppleDevice() {
	return (/iphone|ipod|ipad/gi).test(navigator.platform);
}
function iOSNewerThan(majorVersion) {
	if(isAppleDevice()) {
		// Check the version
		var pattern = /iPhone OS (.*) like Mac/;
		var result  = navigator.userAgent.match(pattern); // Returns "iPhone OS X_Y like Mac, X_Y"
		var version = result[1].split(''); // Returns X, Y
		var release = version[0];
		return (release >= majorVersion);
	}
	return false;
}

// When we're ready to go...
$(document).ready(function() { 
	if(hasRetinaDisplay() && iOSNewerThan(5)) { 
		var highResSplash = '<link rel="apple-touch-startup-image" href="/images/splash-iphone4.png" />'; 
		$('head').append(highResSplash); 
	}
});

So this is our CSS:

<link rel="apple-touch-startup-image" href="/images/splash-ipad-landscape.png"  
media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)" />

<link rel="apple-touch-startup-image" href="/images/splash-ipad-portrait.png" 
media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)" />

<!-- iPhone 4 devices running iOS5+ will get a high-res one via magic JS -->
<link rel="apple-touch-startup-image" href="/images/ios/splash-iphone.png" media="screen and (max-device-width: 320)" />

To sum up:

  • iPads will get the a splash screen for each orientation,
  • non-retina display iOS devices will get the lower quality image,
  • iPhone 4's running iOS4 will get the lower quality image (sadly), but
  • iPhone 4's running iOS5 or above will get a glorious, high-quality image (depending on your design skillz of course).

Hopefully Apple will sort this out in iOS5 and add support for the sizes attribute to the splash screen (or something cleverer). As it stands it appears to be very much an unsupported feature but, getting a high-res splash image on my phone? Totally worth it.

Posted
AuthorPaulo Fierro
After watching the Microsoft Courier videos on Engadget yesterday I started thinking about radial menus again. There are a few examples out there for ActionScript but I wanted to figure it out in Cocoa Touch.

So this morning I spent a couple hours throwing some code together. Basically this was the idea:

  1. Listen for touches
  2. On touch-start, start a timer to show the menu
  3. On touch-end, kill the timer. The user will have lifted their finger so we don't want to show the menu
  4. If we're showing the menu, start a timer to remove it once its shown
  5. If a button is tapped or the timer is triggered remove the menu
  6. If a button is tapped, do something

At the end I tossed in some animation to make it a little nicer visually. A quick vid of it is below and you can grab the Xcode project here. Its obviously not perfect, but shows shows the general idea.

Posted
AuthorPaulo Fierro

Sometimes you see something so obvious, yet totally original that just seems like the "proper" way of doing it. The latest case for me was in atebits' Tweetie 2 for iPhone. The tab bar has a tiny arrow that animates to indicate the currently selected tab item. In addition to a custom, yet standard indication by turning the icon blue-ish this small animation just makes it even more clear to the user where they are.

So in the tradition of stealing great ideas I tried to find out how to do this. Queue Picasso quote here. Anyways, turns out it was fairly simple - it came down to:

  1. creating a view to contain a TabBarController and be its delegate
  2. adding an indicator image to the TabBarController's tab bar
  3. animating the image to the selected tab bar index when the TabBarController's didSelectViewController method is fired

I created a small Xcode project (following the NIB-less way) that shows how I cloned it. You can see it in action below:

You can download the Xcode project here. Next up is the pull-down to reload mechanism :)

Posted
AuthorPaulo Fierro

After doing this whole iPhone development thing for a little over a year now I've realized that I hardly ever use Interface Builder. Sometimes I'll create a dummy NIB, drag in some control and use the panels to find what certain things are called which makes it easier to search through help. But otherwise I simply don't use it, kind of like in Dreamweaver or FlexBuilder where I never use the Design view - eventually you start to render this stuff in your head.

Which I suppose is a little scary.

Anyways, another thing I found was that I never really grokked the whole Interface Builder way of dragging outlets back and forth - I'd often drag something wrong and the whole project would just stop working. Finding out what had gone wrong wasn't always the easiest thing in the world.

So I decided to figure out exactly how I could go about avoiding using Interface Builder at all. You can create your own custom UIViewControllers etc, using nothing but code which is a nice start - and, maybe its subjective, but I do feel like they run faster. Maybe its just me.

But the problem was MainWindow.xib. The default NIB, how do I get rid of it? I wanted pure non-NIB based project which may sound silly but hey.

Eventually I found out there are three things you need to do:

  1. Delete MainWindow.xib from the project and send it to Trash. Easy enough
  2. In your project's Resources group, find the Info.plist file remove the entry for "Main nib file base name". Delete the line and save the file
  3. In the Other Sources group, find main.m and modify the following line:
int retVal = UIApplicationMain(argc, argv, nil, nil);

Change this to

int retVal = UIApplicationMain(argc, argv, nil, @"PROJECTAppDelegate");

So if your project is called Cheese, it would be @"CheeseAppDelegate".

Now the project is NIB-less, but because the window is no longer being created in MainWindow.xib we have to do this in code, but its simple enough. In the AppDelegate.m file applicationDidFinishLaunching might look like this:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}

Just add the following line before hand to create it:

window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

For me at least I now feel like I have full-control over what's going on in the project and I sleep better at night. It kinda feels like during a pure AS3 project.

Maybe its completely silly but I'll find out in time :)

Posted
AuthorPaulo Fierro

Great quote from an Art of the Title Sequence interview with Jim Capobianco on the end credits of Wall-E:

Always move.

Don't sit around waiting for approval or something to happen. I have found there is always something to do on your project. Even if it is the smallest thing it keeps the momentum going and momentum is everything. By always moving it gets you that much closer to getting the project done and you stay ahead of the people who feel it is there job to judge and can put a stop to what you are trying to say before you've had a chance to say it.

Like a shark keep moving or die.

How very true.

Posted
AuthorPaulo Fierro