iOS Geo-Fencing in TimeTraveler – Part 2 of 3
My heart sank deeper as more test results came pouring in. The geo-fencing feature in an QA build of TimeTraveler wasn’t working as planned, and I was clueless.
Here’re some of the reports my teammates recorded:
Exhibit #1: Walking trip from SOMA to Ferry Building, San Francisco.
Exhibit #2: Bus trip from City Hall to Jackson Square.
Exhibit #3: Could not trigger alert afterwards, no matter how close to POI.
You can imagine how I felt back then. The test couldn’t have gone any worse. I had but one night to troubleshoot the problem (raising any alarms would postpone further QA work and wouldn’t win me any favors), so there was no time to moan.
Dissecting the Problem:
In a way, it was good that the error reports were so similar. Common issues ran through the exhibits above included delayed notifications, notifications not being triggered and the wrong notifications being triggered.
Session 303 did not go into depths about how geo-fencing was implemented – in particular, when and how locationManager:didEnterRegion gets invoked. As usual, I made the most common mistake a novice programmer would – started writing code without reading the freaking manual. Worst, I had other team members pay the price of this carelessness by having them running half way across the city testing out an unproven feature. It was time to pay the due and re-learn what the technology had to offer from the source.
Fortunately, the documentation did come to my rescue – some points really jumped out on me as I was reading through the page. They seemed to touch on, if not explain, all the issues we were facing.
“Monitoring of a region begins immediately after registration. However, do not expect to receive an event right away. Only boundary crossings can generate an event. Thus, if at registration time the user’s location is already inside the region, the location manager does not generate an event. Instead, you must wait for the user to cross the region boundary before an event is generated and sent to the delegate”.
“…the user’s location must cross the region boundary and move away from that boundary by a minimum distance and remain at that minimum distance for at least 20 seconds before the notifications are reported”.
“…if Wi-Fi is disabled, region monitoring is significantly less accurate. However, for testing purposes, you can assume that the minimum distance is approximately 200 meters.”
“…an app can expect to receive the appropriate region entered or region exited notification within 3 to 5 minutes on average, if not sooner.”
The obvious takeaways from the excerpts above were:
- Triggering is based on “boundary crossings”, not current location. That means when you’re already inside a geo-fence, the didEnterRegion method would not be invoked.
- Notification is not instantaneous.
- Monitoring isn’t dead-on accurate but has a loose bound of 200 meters.
2. BAD SETTINGS
The fact that notifications weren’t being triggered most of the time got me thinking that something fundamental, likely configuration, went awry. That further got me to open up the plist file in Xcode to check if the correct parameters were in it, namely UIBackgroundModes.
Now, before reading on, please take a longer look at the plist file above, especially the highlighted section. At first, it’s unlikely you would notice anything out of the ordinary. After all, the keys and values were auto-completed by Xcode. However, I have grown really suspicious of plist files over the years and knew better how to debug them – in text mode.
Voila! Keys in plist files aren’t meant to be legible, and the “Required Background Modes” value looked waaayy too out-of-place. Shouldn’t that key be named “UIBackgroundModes“? I went ahead and made the amendment while in text view. Out of curiosity, I switched back to formatted view and see how Xcode has handled the change.
Apparently that edit triggered something prominent. No longer was the value App registers for location updates or the key Required Background Modes, Xcode has updated them to location and UIBackgroundModes respectively. In the formatted view, I noticed how the “B” in Background became lower case. It seemed that the auto-completed suggestion from Xcode was incorrect right from the start – causing the app to not receive any location updates and rendering the entire geo-fencing feature broken!
3. GEO-FENCE TOO SMALL
Initially I set the geo-fence radius to be 30 meters, which in a densely populated city like San Francisco might be the typical length of an entire block. From #1 RTFM, it turned out iOS cannot yet handle locations to such granularity and 200 meters is considered a minimum. So that was one change I made immediately.
4. NEED TO DO OWN-TRIGGERING
The biggest limitation in iOS’s implementation of geo-fencing was the mandate for boundary crossing - the imposition that a user has to enter or exit the borders of a monitored region to receive any type of triggering. I believe this restriction was imposed to prevent false positives, but it might be too stringent for a lot of real world use cases. For instance, if I want to know immediately whether a certain ice cream parlor is nearby, I shouldn’t be asked to step two mile away then return later. That just doesn’t make sense.
The workaround needed here would be for the app to do its own triggering to handle cases ignored by iOS by default. When running in foreground or background, the app could register for location updates and execute the needed logic if the current location lies within a monitored region radius. After firing the notification, it would have to unregister the region from monitoring as well.
As with all workarounds there was a caveat. The app would need to be running in foreground or background mode for this logic to execute. On the other hand, the concern for battery drain is not too great, if the AutoPause feature in iOS 6 works as advertised.
Putting it all to test
All that observation and patches were done from the comfort of my laptop with help from the iOS simulator. Armed with a new build, I headed up to the city to do some field testing of my own. The outcomes of the test would decide whether the whole geo-fencing idea was a stroke of genius or a huge waste of everyone’s time.
In the next post I’ll document how the testing went and what new insights we managed to gain while shouting and waving our iPhones across the streets of San Francisco.