◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-04-27Intermediate

Five Things to Check First When Geolocation Stops Working in Your Rork App

Geolocation looks easy until it doesn't work. This guide walks through the five most common reasons your Rork app can't read the user's location — from a missing app.json plugin block to subtle simulator quirks.

troubleshooting69geolocationexpo-locationpermissions7rork59

When you ask Rork to "build me a nearby-shop finder" or "build me a running tracker," the AI happily wires up expo-location or react-native-geolocation and the code looks reasonable. Then you run it on a real device and nothing happens. No coordinates. No permission dialog. Sometimes a silent timeout, sometimes a flat-out crash. I've spent more than half a day chasing this on a single project, and the punchline is almost always something other than the JavaScript itself.

Location is one of those features where the code can be perfect but a missing line in app.json, a simulator setting, or an OS-level toggle stops everything. This article walks through the five checks I now run, in order, whenever a Rork-generated app refuses to deliver coordinates.

1. Figure out whether the dialog is even showing

Before anything else, separate two very different problems: "the dialog appeared and the user denied it" versus "the dialog never appeared at all." The fixes are completely different.

On iOS, open Settings → your app → Location. If the row is missing entirely, your app has never asked for location permission. On Android, go to Settings → Apps → your app → Permissions and check the same way.

// Log the current permission state so you can tell which case you're in
import * as Location from 'expo-location';
 
async function checkLocationPermission() {
  const { status, canAskAgain, granted } = await Location.getForegroundPermissionsAsync();
  console.log('current state:', { status, canAskAgain, granted });
  // Expected outputs:
  // { status: 'undetermined', canAskAgain: true, granted: false } -> never requested
  // { status: 'denied', canAskAgain: false, granted: false }     -> denied, can't reprompt
  // { status: 'granted', canAskAgain: true, granted: true }      -> good to go
}

If status is undetermined and you've never called requestForegroundPermissionsAsync(), jump to point 3. If it's denied with canAskAgain: false, you can't reprompt from code — your only path is a button that opens system settings.

2. The missing app.json plugin block (by far the most common cause)

In my experience, this is the single biggest reason Rork-generated apps silently fail at location. Just installing expo-location doesn't add the iOS usage descriptions, so the build succeeds but the dialog never appears on a real device.

Add the plugin block to app.json:

{
  "expo": {
    "plugins": [
      [
        "expo-location",
        {
          "locationAlwaysAndWhenInUsePermission": "We use your location to track runs in the background and on screen.",
          "locationWhenInUsePermission": "We use your location to show nearby shops.",
          "isIosBackgroundLocationEnabled": false,
          "isAndroidBackgroundLocationEnabled": false
        }
      ]
    ]
  }
}

The wording matters. Apple's review team will reject vague strings like "we use your location" — you need a sentence that makes the user's brain say "okay, that's reasonable." Tie the explanation to a concrete feature: tracking a run, showing nearby shops, calculating delivery distance.

After updating app.json, Expo Go will not pick up the change. You need to run npx expo prebuild --clean to regenerate the native projects and then build a fresh dev or production binary with EAS Build. Forgetting this step is what makes people swear they "fixed the JS but it still doesn't work."

3. Asking for permission too early

Rork tends to put the permission request inside the top-level useEffect, firing it the second the app launches. It works, but it's a bad UX choice — and on iOS, dialogs that appear before the user has done anything tend to get reflexively denied.

// Bad: hits the user with a dialog the moment the app opens
useEffect(() => {
  (async () => {
    await Location.requestForegroundPermissionsAsync();
  })();
}, []);
 
// Good: ask only when the user taps "use my current location"
const handleUseCurrentLocation = async () => {
  const { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== 'granted') {
    Alert.alert(
      'Location needed',
      'To search near you, please allow location access in Settings.',
      [
        { text: 'Cancel', style: 'cancel' },
        { text: 'Open Settings', onPress: () => Linking.openSettings() },
      ]
    );
    return;
  }
  const location = await Location.getCurrentPositionAsync({});
  setLocation(location);
};

Tying the request to a clear user action — a "find nearby" button, a map screen, a "start run" tap — typically doubles or triples the grant rate compared to asking on launch.

4. Works on the simulator, fails on real devices

If your code works in the simulator but breaks on a phone (or vice versa), one of these is almost always the culprit:

  • iOS simulator location is set to None. In the simulator menu, Features → Location must be set to something other than "None" for the simulator to return coordinates. "Apple" or "City Run" gives you a moving fake location.
  • Real device with weak signal indoors. GPS cold-start can take 10+ seconds indoors. A 3-second timeout will fail nearly every time. Set getCurrentPositionAsync({ timeout: 15000 }) or fall back to a network-based lookup.
  • Android device-wide location services off. On Android, app permission can be "granted" while the device's overall location switch is off. You need Location.hasServicesEnabledAsync() as a pre-check.
async function getLocationSafely() {
  // Android: is the device-wide location switch on?
  const enabled = await Location.hasServicesEnabledAsync();
  if (!enabled) {
    Alert.alert('Location services off', 'Please turn on location services in your device settings.');
    return null;
  }
 
  const { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== 'granted') return null;
 
  try {
    const location = await Location.getCurrentPositionAsync({
      accuracy: Location.Accuracy.Balanced, // good enough for most map use cases
      timeout: 15000,
    });
    return location;
  } catch (error) {
    console.error('location failed:', error);
    return null;
  }
}

I'd avoid defaulting to Location.Accuracy.Highest. It drains battery and is much more likely to time out indoors. For a "show me on a map" feature, Balanced or High is plenty.

5. The background-location trap

For features like "keep tracking a run when the screen is off" or "follow a delivery driver in the background," foreground permission isn't enough. This is where things get noticeably harder, and Rork's initial scaffold rarely covers it.

On iOS you need isIosBackgroundLocationEnabled: true in your plugin config and UIBackgroundModes including location in the Info.plist. The user also has to choose "Always" — and Apple's review process scrutinizes background location more than almost any other API. If the user has only granted "While Using," background-location calls will silently return nothing.

// Always request foreground first, then background
async function requestBackgroundPermission() {
  const fg = await Location.requestForegroundPermissionsAsync();
  if (fg.status !== 'granted') return false;
 
  // On iOS, a brief delay between the two prompts improves grant rates
  await new Promise((r) => setTimeout(r, 500));
 
  const bg = await Location.requestBackgroundPermissionsAsync();
  return bg.status === 'granted';
}

For App Store review, the reason for background location must be obvious from the feature itself. If you don't actually need "Always," strip it out — your reviewer will be happier and your users will grant foreground permission far more readily.

A quick checklist for next time

When location stops working, run through these in order:

  • Log getForegroundPermissionsAsync() and read the status
  • Open Settings on the device and check whether the app's Location row exists
  • Verify the expo-location plugin block in app.json is correct
  • Confirm you've run npx expo prebuild --clean and rebuilt with EAS
  • Check the simulator's Location setting, or the device's location-services toggle
  • Bump the timeout to ~15 seconds and lower accuracy to Balanced
  • Reconsider whether you actually need background location

Location bugs feel mysterious because the failure mode is so often silent. Working through the list above turns a half-day debugging session into a 15-minute checklist.

If you want to think more broadly about permissions in Rork apps, our Rork Permissions Troubleshooting Guide covers the same patterns for camera, notifications, and photo library — the ideas transfer directly.

The fastest move you can make today is to drop a single console.log of getForegroundPermissionsAsync() into your app and run it on a real device. That one log usually points straight at which of the five issues above is biting you.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⬡ Dev Tools2026-10-07
Japanese Headings Won't Turn Bold on Android: How to Trace and Fix a fontWeight That Does Nothing
A troubleshooting note for Rork apps where headings look bold on iPhone but stay thin on Android. How to tell the symptoms apart, pick weights through fontFamily, and verify the fix.
⬡ Dev Tools2026-08-18
Version code 1 has already been used — who owns that number, app.json or EAS?
When a Play upload stops on versionCode, check where the number actually lives before bumping it. A one-command way to tell whether app.json, EAS, or build.gradle wins.
⬡ Dev Tools2026-05-26
iOS Foreground Notifications Disappear in Expo — A Practical Fix Guide
A focused walkthrough of why iOS silently drops push notifications when the app is in the foreground, and how to wire setNotificationHandler correctly for iOS 14+ with expo-notifications.