CoreLocation for Mac is Broken.

Sort of surprised that I haven’t written about this yet, actually… considering the thing has been a thorn in my side forever.

In case you’ve been living under a rock, CoreLocation is Apple’s framework for finding a user’s geographical location based on either nearby WiFi networks, or cell radio towers.  It was originally developed for iOS and then ported over to the Mac in 10.6.

In the case of the Mac, there’s no way for it to see cell radio towers (yet), so it’s based strictly off of WiFi networks.  It’s still surprisingly accurate in most cities.

The whole process of getting a list visible WiFi networks, sending that up to Apple’s server, and getting back a location works just fine.  But here’s the problem… a typical Location-aware app doesn’t just ask for a user’s location once.  It generally wants to know about any updates to a user’s location.  CoreLocation’s API is built for this.  It sends multiple messages to the delegate as time goes on to alert it of new or more accurate positioning.

This by itself isn’t an issue.  The issue lies in the fact that it’s locationd’s (the daemon backing CL) responsibility to figure out when it’s appropriate to check for a new position.  It’s a somewhat tricky problem to solve, one which I’m familiar with because I had to write an implementation of it back when we used Skyhook’s WPS library instead of CoreLocation as it didn’t have that functionality.  

The list of options:

  • Only when explicitly requested.  Well that works out nicely for one-offs, but not for apps that need new locations reported.
  • Every x seconds.  OK so what’s x?  How far is a Mac going to travel in 1 second?  Probably not too far, considering they need a net connection to do the request, and most ethernet cables aren’t THAT long… and WiFi only has so long of a range.  So that’s too short, and going to be really excessive.  Every 10 seconds?  There must be a better way.
  • Upon wake.  How often are you going to travel more than 50m without first putting your Mac to sleep?  But you’ll probably have to wait a few seconds after wake to do it, since usually no network is available for a while.
  • When connecting or disconnecting from a WiFi network.  Since it’s based on WiFi networks, and we can get notifications when the airport connects/disconnects from a network, that might be a good time to do it.  In the case of disconnecting from a network, you’ll need an alternate network (wired) to do the request.  And when connecting, you’ll need to wait for the network to actually be up to do the request.  Better hope there’s no authentication needed to get onto the network (something web-based; can’t believe those systems are still out there), cause building around that is another pain.

OK so they all have issues.  Here’s how I did it:

  • When explicitly requested, go and do it.
  • When connecting or disconnecting from WiFi, wait for a valid net connection to be available (up to n seconds), and do the request.

This worked fine for the case of NetworkLocation and Jet Lag.

I don’t know what logic Apple uses.  For most users, it seems to work fine.  For some, it’s a disaster though.  locationd is seemingly in a perpetual loop, constantly redetermining the user’s location.

Why you don’t want to do this too often

Getting a list of visible WiFi networks requires that OS X put the Airport card into passive mode to listen on all channels.  When it’s in passive mode, it can’t be transmitting data.  Then once it has the list, it has to connect to Apple and transmit its list.  So you’re essentially shutting down network services, and then doing a network call.  This all happens pretty quickly, so users don’t tend to notice it.  But when it’s constantly happening, it starts to become a pain.

How do I know this?

I’ve actually never seen this myself.  My users have, though.  Consistent reports from a small percentage of NetworkLocation users: when they run NetworkLocation the audio to their Airport Express speakers stutters.  Or they notice a large amount of network traffic specifically to mac-services.apple.com (god bless users who send me tcpdumps).  We’re talking many GB of traffic per day here.  For users on metered connections, or who are tethering, that’s _unacceptable_.  As soon as I get them to turn off Location Services the problem goes away.

How do I know it’s not just NL misbehaving?

We’ve written a dummy app that we used to send to these users.  All it did was launch a window and register itself with CoreLocation.  That’s it, that’s all.  The problem was present.  This wasn’t necessarily enough to convince some of them, understandably.  The real smoking gun is that if I told them to shut down all apps of ours, but then enable “Set time zone automatically using current location” in System Preferences (i.e. Jet Lag for Snow Leopard), the problem would come back.  

Workarounds

I don’t have any good workarounds, just a few bad ones:

  • Don’t use CoreLocation.  I tell these users to turn off Location Services under the Security pane of SysPrefs.
  • Make your app smart enough to disconnect itself from CoreLocation when not needed, and reconnect later.  So basically, you need your own logic for when to do the scanning.

NetworkLocation v4 will have much better smarts for this which I’m hoping will solve the issue for our users.

Of course, if they’re using “Set time zone automatically using current location” then the workarounds don’t matter, because that app doesn’t disconnect.

Radar

I’ve had an open radar about this since Nov 9, 2009.  rdar://7377614  I’ll be adding a user note to it with a link to this blog post.

Short URL for this post: http://tmblr.co/ZhqSVy2Q24PN