Resetting Location Permissions on macOS for Xcode Testing

Lately I came across something awful to debug on macOS, and I wanted to share the "solution" I found. When debugging location services, I needed to test three distinct states:

  1. First launch - "Enable Location Access" prompt (never asked before)
  2. Denied - User allowed Location Access already, but for some reason hasn't granted permission. User needs to go to System Settings > Privacy & Security > Location Services, then toggle the option for the app to enable it.
  3. Granted - Location access is working

The problem? macOS makes it incredibly difficult to reset back to state #1. I tried erasing Xcode caches, deleting app preferences, and much more (I'll list some commands below), but nothing worked. No AI helped right away, so I searched the web and as always someone was already frustrated with this. I'll list references at the end.

Basically, I found that a file named clients.plist in /var/db/locationd/ stores the location permission states for apps. And we can't remove apps from it easily. Deleting or editing would solve the problem. But you can't simply delete the clients.plist file from a normal boot, even with sudo. The system protects this file. It makes sense from a security and privacy perspective, especially coming from Apple, but it sucks for developers.

There are some partial solutions that would probably work:

  1. Change the bundle identifier for each build. This is actually a nice workaround, but I was looking to test only 1 or 2 times. I didn't need to test it dozens of times. This solution would make sense in that scenario, even though I don't know the implications and how to reset everything for each bundle (like clean state for that bundle). Probably not hard though.
  2. Disable System Integrity Protection (SIP) temporarily, then delete or edit the clients.plist file. This is possible, but I didn't want to mess with SIP on my main development machine.
  3. Temporarily bypass SIP via Recovery Mode. This is the solution I ended up using, and it worked perfectly.

The Solution: Recovery Mode

The only reliable way I found to reset location permissions completely is through Recovery Mode:

  1. Restart your Mac and hold Command + R (Intel) or hold the power button (Apple Silicon) to enter Recovery Mode
  2. Go to Utilities > Terminal
  3. Run these commands:
cd /Volumes/Macintosh\ HD/var/db/locationd/
plutil -convert xml1 clients.plist
vi clients.plist
  1. In vim, search for your app: /MyApp (your app name)
  2. Remove the entire entry from <key> to </dict>
  3. Save and exit (:wq)
  4. Restart your Mac

This will reset your app's location permission state to "never asked." (Just be careful what you modify when editing system files!)

Useful Helper Commands

Reading more about Xcode development cleanup, I found some useful commands to help clean caches and app states. These didn't quite solve the problem above, but they're useful to have around.

Xcode Cache Cleaning

# Xcode cache cleaning (safe - just caches)
alias cleanXcodeCache='rm -rf ~/Library/Developer/CoreSimulator/Caches && \
rm -rf ~/Library/Developer/Xcode/UserData/Previews && \
rm -rf ~/Library/Developer/Xcode/Archives && \
rm -rf ~/Library/Developer/Xcode/Products && \
rm -rf ~/Library/Caches/org.swift.swiftpm && \
rm -rf ~/Library/Caches/com.apple.dt.Xcode && \
echo "โœ… Xcode caches cleaned"'

Other Useful Commands

# Reset location permissions for a specific app
tccutil reset All com.mycompany.MyApp

# Restart location services daemon
sudo killall -HUP locationd

# Find all instances of your app (useful when debugging to ensure clean slate)
find ~ -name 'MyApp.app' -type d 2>/dev/null

Bash Function to Clean App State (hard reset)

# Clean specific app state + Xcode caches
#
# ATTENTION that this will delete app data!
#
# Usage: cleanapp [bundle_id] [app_name]
# Example: cleanapp com.mycompany.MyApp MyApp
cleanapp() {
    local bundle_id="${1:-com.mycompany.MyApp}"
    local app_name="${2:-MyApp}"

    echo "๐Ÿงน Cleaning: $app_name ($bundle_id)"
    echo ""

    # Kill running instances
    pkill -f "$app_name" 2>/dev/null && echo "  โœ“ Killed running app"

    # DerivedData for this app
    rm -rf ~/Library/Developer/Xcode/DerivedData/${app_name}-* 2>/dev/null && echo "  โœ“ DerivedData"

    # App preferences (UserDefaults)
    rm -f ~/Library/Preferences/${bundle_id}.plist 2>/dev/null && echo "  โœ“ Preferences plist"

    # Also try without com. prefix
    rm -f ~/Library/Preferences/${bundle_id#com.}.plist 2>/dev/null

    # App containers (sandbox data)
    rm -rf ~/Library/Containers/${bundle_id} 2>/dev/null && echo "  โœ“ Containers"

    # App saved state
    rm -rf ~/Library/Saved\ Application\ State/${bundle_id}.savedState 2>/dev/null && echo "  โœ“ Saved state"

    # Kill preferences daemon to flush
    killall cfprefsd 2>/dev/null && echo "  โœ“ Flushed preferences daemon"

    echo ""
    echo "โœ… App state cleaned!"
}

Hope this saves someone the hours I spent figuring this out. If you know a better way, let me know!

References