DEV Community

Cover image for The location bugs the iOS Simulator will never show you
PinDrift
PinDrift

Posted on

The location bugs the iOS Simulator will never show you

Here's a fun way to lose an afternoon: ship a feature that worked perfectly in the Simulator, then watch the first bug report come in from someone standing in a parking garage.

The Simulator is great at a lot of things. Location isn't really one of them. It hands your app a clean coordinate, at a steady rhythm, with perfect accuracy, forever. Nothing on a real phone works like that, and most of the location bugs we've seen live in exactly that gap.

We spend a lot of time staring at Core Location output, so here's the list we wish someone had given us earlier.

  1. Speed and course can be "I don't know" CLLocation has speed and course properties, and it's tempting to just read them. But both can be negative, and negative means invalid. Not "going backwards," just "no data."

You'll see this constantly when the phone is standing still, right after a cold start, and with simulated locations in general. If you plot a speed of -1 km/h or rotate a heading arrow to -1 degrees, nobody's going to report it nicely. They're just going to think your app is broken.

func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
guard let loc = locations.last, loc.horizontalAccuracy >= 0 else { return }

let speed  = loc.speed  >= 0 ? loc.speed  : nil  // -1 means "no idea"
let course = loc.course >= 0 ? loc.course : nil

// Only draw what you actually know.
Enter fullscreen mode Exit fullscreen mode

}
Same rule for horizontalAccuracy: if it's negative, don't trust the coordinate at all.

One more thing about course: a person standing still doesn't have a heading. If your arrow follows course, it'll snap to north (or wherever) the moment someone stops walking. A GPX route in Xcode never stops, so you won't see this until you're outside.

  1. Accuracy is a range, not a promise In the Simulator, every fix is accurate to the meter. In real life your app will eventually get a fix with a 300 meter radius, maybe more. Indoors, underground, between tall buildings, on a train.

The question isn't whether that happens. It's what your UI does when it does. Do you show "0.1 miles away" based on a fix that could be off by a fifth of a mile? Do you trigger an arrival? Decide on a threshold and write it down, because "we'll handle it later" usually means a user finds it first.

  1. Approximate location is a real state Since iOS 14, users can grant location but turn off "Precise." A lot of people do, because the toggle is right there in the prompt.

With reduced accuracy you get fixes measured in kilometers, updated much less often. If your feature needs better than that, you can ask for temporary full accuracy:

if manager.accuracyAuthorization == .reducedAccuracy {
manager.requestTemporaryFullAccuracyAuthorization(
withPurposeKey: "NearbyStores" // must exist in Info.plist
)
}
The key has to be listed under NSLocationTemporaryUsageDescriptionDictionary in your Info.plist, or the request quietly does nothing.

And the user can say no. That's the path nobody tests, so test it: what does your screen look like when you only know the city?

  1. Your app gets suspended This is the big one. On a phone, iOS suspends your app, throttles it, and wakes it up when it feels like it. The Simulator mostly doesn't.

A few things that catch people:

pausesLocationUpdatesAutomatically is true by default. If the user sits still for a while, iOS may pause your updates, and they won't resume on their own the way you'd expect. For a fitness or navigation app, that's a bug report waiting to happen.
Background updates need the background mode turned on and allowsBackgroundLocationUpdates = true. Forget either one and it works fine in the foreground and silently stops in the background.
Geofence events can relaunch your app from scratch. Your handler needs to work with no view controller loaded and nothing in memory.

  1. Geofences are fuzzier than you think If you use region monitoring, test it on a device. There's no way around this.

There's a limit of 20 monitored regions per app. Region number 21 just doesn't get monitored.
Crossings aren't instant. iOS waits to be fairly sure you've actually crossed before it tells you.
Small radiuses don't work well. A 50 meter geofence won't fail loudly. It'll fire late, or once, or when the user is already past it.
If your product depends on "notify me when I arrive," budget real time for this.

  1. A fake coordinate doesn't move the rest of the phone When you simulate a location, you change one number. Everything else stays put:

IP address. Your backend still sees a connection from your own city. If the server geolocates by IP, it'll disagree with the coordinate your app just sent. (Real users on a VPN cause the same thing, so it's worth testing anyway.)
Time zone. iOS doesn't change it based on a simulated location. If you format times for "where the user is," you need a separate test for that.
Wi-Fi and cell networks. Still your office. Any SDK that reads network info will see the truth.
How we'd test it, if we were starting over
Use all three tools, and know what each one is for:

The Simulator for layout and the permission flow. Two commands make this much faster:

Put the simulator somewhere specific

xcrun simctl location booted set 38.7223,-9.1393

Reset the location permission so you can test the first-launch prompt again

xcrun simctl privacy booted reset location com.example.yourapp
The second one saves you from deleting and reinstalling the app every time you want to see the prompt.

GPX files for anything involving movement: distance, heading, speed. Just remember that Xcode paces the playback by how far apart your waypoints are, so a realistic speed means spacing them carefully. And a hand-drawn route is always smoother than a real walk.

A real device for everything that matters: accuracy, suspension, battery, geofences. On iOS 16 and later you'll need Developer Mode turned on first. Note that a location you set from Xcode only lasts while the debugger is attached, so anything that takes hours (an overnight background run, a day of battery use) needs a different setup.

Then keep a small list of coordinates that have broken things before and run them every release. Good starting points:

0, 0 and points near the equator and prime meridian, for sign bugs
Both sides of the 180th meridian, for wraparound bugs
Somewhere far north, for map projection assumptions
A spot in the middle of the ocean, for reverse geocoding that returns nothing
None of this is glamorous. But almost every location bug we've run into would've been caught by one of the items above, and almost none of them would've been caught by the Simulator alone.

If you've got a location bug story of your own, drop it in the comments. We collect them.

Top comments (0)