GeoFencer – Open Source App

I have just put the source code for an app that I’ve been working on for a couple of months  up on Github. The idea is similar to existing functionality in the iPhone, where you can schedule location based reminders.

My app allows you create the geofence based on mapping functionality, set a  radius [which I’ll back to later] and then an action. Currently there are three supported actions: local notification, email, and Wake on LAN.

The app runs in the background but using a power saving mechanism that Apple has implemented. Rather than running all of the time, the app is registered to receive ‘significant change’ events based on cell tower locations. As this isn’t very regular, and depends on cell tower density in the area that you happen to be in, I’ve set the geofence regions to be anything up to 500 metres in diameter.

The part that I had the most sport with was the Wake on LAN functionality, which I’ve written about before. One thing that initially stumped me was why an IP was sometimes in the arp table, and sometimes not. The penny dropped that it will be in there if the device has tried to make a connection previously. So in order to guarantee this, I send a dummy UDP packet – 100 times, just to be sure.

This introduces an interesting problem: the regions are – well, on paper they are circular, but in reality, due to the significant change implementation, are actually going to have ragged boundaries. The operating system will [hopefully] recognise that you have entered the region, at which point you will probably not be on your home network. The use case for this was to be able to turn on my NAS device when I got in the front door. So if the action is a Wake on LAN event, I request 10 minutes running time from the operating system using beginBackgroundTaskWithExpirationHandler, and then every 30 seconds try to identify if we are on the home network. I repeat this 19 times [rather than 20], because the delay I use didn’t seem to be very precise [sleepForTimeInterval]. If the home network is detected, I then send a flood of WoL packets at a previously configured IP address.

While the construction of the WoL ‘magic packet’ is all fairly standard stuff, unfortunately the only way I could find of converting the IP into the MAC address required the use of private APIs. This, and the fact that the app can send emails in the background, meant that the final result was never going to be heading towards the App Store.

I use my phone very heavily, and it’s also getting on for a couple of years old now, so it’s hard to make a definitive call on what the battery life impact is. I’d estimate that it’s probably about a 5% penalty, something of that order of magnitude.

Reliability is another matter. The delegates to trigger the significant change seem to ‘go away’ every now and then, and I haven’t gotten to the bottom of this yet. This is why one of the features I’ve implemented is to log ever significant change event – something my 45 minute train journey is quite useful for.

My working assumption is that the operating system makes decisions about whether or not it will honour the delegates, based on resource consumption at a given time.

So it’s done. It’s pretty rough around the edges [the UI is something of a tragedy], and my code probably breaks every convention that Objective C requires, but it works.