A native macOS application for overriding geolocation data reported by web browsers and the operating system. Built with SwiftUI, targeting macOS 14 (Sonoma) and later.
Developed by Terabitlab.
SpoofGeo intercepts and replaces the geolocation coordinates returned by browser APIs (navigator.geolocation) with user-defined values. It supports Chromium-based browsers via the Chrome DevTools Protocol (CDP) and Safari via AppleScript JavaScript injection.
The tool is intended for software testing, development, and research scenarios where deterministic geolocation data is required.
| Spoof View | Test View |
|---|---|
![]() |
![]() |
| Apps View | Browser Result |
|---|---|
![]() |
![]() |
SpoofGeo/
├── SpoofGeoApp.swift # Application entry point (@main)
├── Views/
│ ├── MainView.swift # Tab navigation (Spoof / Test / Apps)
│ ├── SpoofView.swift # Map interface, coordinate input, search
│ ├── TestView.swift # Location verification, diagnostic report
│ └── AppsView.swift # Per-application spoof management
├── Services/
│ ├── LocationStore.swift # Central state (ObservableObject), spoof lifecycle
│ ├── BrowserSpoofer.swift # CDP + Safari JS injection engine
│ ├── AppLauncher.swift # App launch with DYLD hook or browser spoof
│ ├── DylibManager.swift # Inline Obj-C dylib compilation (CLLocationManager swizzle)
│ ├── SpoofConfig.swift # Shared config file (/tmp/spoofgeo_config.json)
│ ├── SimHelper.swift # iOS Simulator location via xcrun simctl
│ ├── SearchCompleter.swift # MKLocalSearchCompleter wrapper
│ ├── GPXExporter.swift # GPX XML generation
│ └── Coords.swift # Coordinate parsing ([lat, lon] formats)
└── Assets.xcassets/ # App icon, accent color
For Chromium-based browsers (Chrome, Edge, Brave, Arc, Opera, Vivaldi, Chromium):
- The browser is launched with
--remote-debugging-port=9222 - SpoofGeo connects to the CDP endpoint at
http://localhost:9222/json - For each page tab, it opens a WebSocket connection and sends:
Page.addScriptToEvaluateOnNewDocument— registers a persistent script that executes on every navigation, ensuring the override survives page loadsRuntime.evaluate— injects the override into the currently loaded page immediately
- The injected JavaScript replaces:
navigator.geolocation.getCurrentPosition()navigator.geolocation.watchPosition()navigator.geolocation.clearWatch()navigator.permissions.query()(returns{state: 'granted'}for geolocation)
- A periodic timer (3-second interval) re-injects into new tabs
This approach is more reliable than Emulation.setGeolocationOverride, which requires a persistent WebSocket connection per tab and loses its effect when the connection closes.
For Safari:
- SpoofGeo enables "Allow JavaScript from Apple Events" via
defaults write - It enumerates all Safari windows and tabs via AppleScript
- The same geolocation override JavaScript is injected into each tab using
do JavaScript - The override persists until the page is navigated away or reloaded
For non-hardened native macOS applications:
- An Objective-C dylib is compiled inline via
xcrun clang(universal: arm64 + x86_64) - The dylib swizzles
CLLocationManagermethods (location,startUpdatingLocation,requestLocation) - The target app is launched with
DYLD_INSERT_LIBRARIESpointing to the compiled dylib - Coordinates are read from
/tmp/spoofgeo_config.jsonat runtime
For booted iOS Simulators:
- Location is set via
xcrun simctl location booted set <lat> <lon>
| Component | Version |
|---|---|
| macOS | 14.0 (Sonoma) or later |
| Xcode | 15.0 or later |
| Swift | 5.9+ |
The application requires:
- App Sandbox disabled (for
Process()calls, file writes, simctl access) - Hardened Runtime disabled (for DYLD injection support)
- Location Services permission (for
CLLocationManageraccess)
# Clone
git clone https://github.com/<your-org>/SpoofGeo.git
cd SpoofGeo
# Build (Release)
xcodebuild -project SpoofGeo.xcodeproj \
-scheme SpoofGeo \
-configuration Release \
-derivedDataPath build/derived \
CODE_SIGN_IDENTITY="-" \
ENABLE_HARDENED_RUNTIME=NO
# The .app bundle is at:
# build/derived/Build/Products/Release/SpoofGeo.appbash build_dmg.sh
# Output: SpoofGeo.dmg- Open the Apps tab
- Click Spoof next to Chrome — this relaunches Chrome with the CDP debug port
- Navigate to any website that requests geolocation
- Set your desired coordinates on the Spoof tab
- Click Start Spoof — the override is injected into all open tabs
- Click Start Spoof — SpoofGeo automatically enables "Allow JavaScript from Apple Events"
- If Safari was already running, quit and reopen it manually (one-time requirement)
- The geolocation override is injected into all open tabs
- Open the Apps tab
- Click Build Hook to compile the dylib
- Click Spoof next to a non-hardened app — it relaunches with the DYLD hook
- Hardened apps: SIP-protected applications (most Apple-signed apps) cannot be injected via DYLD
- Firefox: No remote geolocation API; requires manual configuration via
about:config - Safari: JavaScript injection does not persist across page navigations; the override is page-scoped
- CDP: Requires Chrome to be launched with the debug flag; existing sessions without the flag cannot be spoofed
MIT License — see LICENSE.
Developed by Terabitlab.



