Posted by Michael Dales on 2010-03-16 20:33:03
Last weekend I had a good time at the first run of WhereCampEU, a two day unconference dedicated to all things location based. I had a good time there, meeting new people, and learning new things. In an attempt to contribute back, I gave a short talk of on how to write my first iPhone location app:
There were lots going on about maps and visualisation, all very cool and interesting, but I'm also very interested in the indirect use of location to aid the user experience in applications. A lot of people use their laptop or mobile phone for both home use and work use, yet neither device acknowledges this duality. What sort of opportunities are their for enhancing the user experience when your application knows the geographic context in which decisions are made and data is entered? Imagine an IM client that knew which people you talked to at work, and which at home, and displayed those users appropriate to where you were at a give point in time. Imagine a browser that notes where you commonly use certain bookmarks, so reorders them to make work related ones more prominent when you're at work, and less so when you're at home.
The mad thing I realised when prepping sample code for my iPhone talk, is that all the APIs you need to do this are available on not only the iPhone, but also in Mac OS X, so there's no reason we can't implement this sort of behaviour today! So, for the hacking competition ran on the second day of WhereCamp EU, I set about to make an easy to use library that lets your application find out with next to no effort, and even more importantly at no extra effort at all from the user, where the user is: at home? at work? or somewhere else? I give you DFGeoContext.
DFGeoContext's interface is simple. You create a context object, and set yourself as the delegate for location context updates:
self.userContext = [[DFGeoContext alloc] init];
userContext.delegate = self;
Then, you simply tell it when you want it to start looking for context information:
[userContext startFindUserContext];
Then, you implement the delegate method that DFGeoContext will call when it's found the user's context:
- (void)userContext: (DFGeoContext*)context
updatedTo: (NSString*)location
{
[displayThingy setStringValue: location];
}
The location value should be one of the following values:
These indicate it the user is at home, at work, some location other, or the service could not work out the current location.
And that's all there is to it!
From the user's perspective, they don't need to do anything. Your application doesn't need any new control or such - all the information needed to make this work is already there - that's the beauty of it. Most users will already have entered, either directly or indirectly on setup, their home address into the system AddressBook. If you're lucky, they'll have added their work details too (assuming they have a work address). DFGeoContext takes advantage of all this existing information so you don't need to pester the user for it.
Under the hood, DFGeoContext simply pulls together three existing APIs:
The first two are native libraries on the Mac and iPhone. CoreLocation lets you get the latitude and longitude coordinates of the device, so we can work out where we are currently, clearly essential. Although everyone knows about CoreLocation on the iPhone, it's actually available on Mac OS X too since Snow Leopard, where it uses local wifi base stations to estimate location. The AddressBook has a very simple API to let applications inspect the user's information, including things like home address and work address.
At this point, it sounds like we're done, but not quite. We need to convert the textual addresses for home and work into latitude and longitude so we can compare them with the information from CoreLocation. Unfortunately not even the iPhone has an API to do forward geocoding - the ability to turn a textual name of a place into coordinates. For that we need to use a third party web service, and for that I've picked to use CloudMade, the commercial friendly API side of OpenStreetMap. What I like about CloudMade, as opposed to say Google and all the other APIs that can also do this is that they've got nice support for both open projects and commercial projects, so you can use DFGoeContext in either. You just need a key, and the commercial rates for API use are actually documented and reasonable, unlike some other companies I've looked at.
Anyway, so we call CloudMade with our addresses and get back coordinates, and we're done. We can then work out if we're at any of the locations in the user's AddressBook, or we're somewhere else. To make getting the demo together easier, I've used the excellent ASIHTTPRequest library for API calls to CloudMade, and the SBJSON framework for decoding the JSON data CloudMade return.
Having hacked this together in an afternoon, I didn't have time to put a slick demo app together - I presented this along with a very simple dialog box interface to test it. However, the audience at WhereCamp EU clearly got the idea, as DFGeoContext won the hacking contest! There was only a small number of entries, but the other entrants made very good demos, so it was quite an honour to win.
There's lots still to do thought with DFGeoContext. I'm really pleased with it in one sense - I've got a library here that requires no effort from the user for applications to start doing sensible things in relation to the context they're in - that's something that excites me, and I look forward to trying to user it in future applications. However, there's some bits that need tidying up still:
But still, not bad work for an afternoon's playing with some APIs. The code is available under an MIT style license on github. It's still a little rough, and I'll tidy it up as I get chance, but I welcome suggestions and feedback on it!
Finally, just a word of thanks to the people who put WhereCamp EU together - well done! It went off smoothly, and had lots of great people and presentations. Looking forward to the next one!