Using iOS’s wonderful Core Location
libraries is easy enough but how can you track a user without launching the application?
I first stumbled upon this problem while researching a simple question: given my velocity and accelerometer data, could I teach a machine what “good” driving habits were vs. “bad” driving habits.
In order to accomplish this I wanted my application to be completely transparent. I had the following requirements for my application.
The application should start automatically whenever it detected I had began driving
It should automatically enter into a lower power mode whenever it detected I had stopped.
It should be resillient to network failures in case I drove somewhere that my signal wasn’t that great.
The application shouldn’t be a burden on the device or battery life.
Trial by Failure, Take 1
I began by researching how to accomplish efficient background networking requests which led me down a stomach turning rabbit hole of despair. iOS does something very well and that is to ensure that battery life is maximized, the user experience is always snappy, and kills / backgrounds any applications it deems unecessary at the time. The latter part was a huge problem for me. As soon as I would get accurate GPS coordinates in the background, iOS would put my application to sleep so that it would no longer save to Core Data
nor would it transmit the data it saved over the network.
Semi-Solution 1: Significant Location Changes
Enter CLLocationManager().startMonitoringSignificantLocationChanges()
. Introduced in iOS 4, this nifty little function does some pretty interesting things ( full docs for more information). This function allows a developer to observe for… well… significant location changes. One of the key take aways from this is directy from the documentation: If you start this service and your app is subsequently terminated, the system automatically relaunches the app into the background if a new event arrives.
So with this new information in hand, I simply created a singleton class to handle all my location updates, requesting user permission, and listening for and saving these location updates.
Trial by Failure, Take 2
Ecstatic that I finally found a solution to my problem of my app not staying awake long enough during my driving sessions, I hastily put together a basic UI and started driving around. Immediately I started noticing problem however. Yes, my app would awaken whenever I traveled a good bit of distance (generally more than 100 meters or so), but it would eventually go back to sleep and never wake back up.
It wasn’t until I discovered that in order for the app to really play nice with iOS, the following needs to happen.
1 | private override init() { |
Semi-Solution 2: Realtime Monitoring
So not only did the app need to wake up whenever significant distance threshold had been crossed, it also needed to actually start monitoring for “realtime” updates. To accomplish this, you can simply ask the location manager to monitor for location changes.
1 | func realtimeUpdates() { |
I setup the desired accuracy, distance filter and then start actually monitoring for location updates. However there is a huge problem with this setup battery drainage!!!!
Yet again, I was met with failure but I was getting closer. I thought to myself, how can I more efficiently track my location? I know that I really want realtime updates WHILE I’m driving but I DON’T want updates whenever I’m idling at a redlight.
Trial by Failure, Take 3
So finally I thought to myself, my primary goal is to analyze my driving behavior holistically and I would be willing to sacrifice loosing some data points if it meant achieving my 4 requirements. Since I decided acceleration data from a stand still (red light) was only a small portion of my driving habits, I used speed
to determine what was idle
and what was not. Arm with these semi-new requirements, I used a property on a CLLocation Object called speed
.
1 | Fair warning, Apple has some recommendations about using the speed property from a CLLocation object as 'informational' purposes only. |
Final Solution
Overall I was able to achieve all my goals and requirements by combining both the significant location monitoring with real time monitoring. Using intelligent pauses, I stopped real time monitoring to save battery and waited for the vehicle to move again before monitoring again.
Code Snippet
1 | // |
1 | // |