Photo Location Sync is a macOS SwiftUI app plus a reusable Swift package for matching Google Maps Timeline export data to Apple Photos assets that are missing location metadata.
Photo Location Sync does not send your imported timeline data or match results to third-party servers. Timeline matching always runs locally on your Mac.
Rich place labels are optional. If the user enables Apple geocoding in Settings, Apple receives anonymized coordinates to return address and place-name labels. If the review map is visible, Apple map tiles may load for the visible region. Photo previews are also allowed to load from iCloud Photos when an asset is not stored locally, so Apple Photos may download image data for display. Separately, when you approve an update, the app writes metadata into Apple Photos locally, and iCloud Photos may sync those approved library changes through Photos after the local write.
Package.swift— SwiftPM manifest for the reusable modules and the macOS app targetApp/PhotoLocSyncMac/— SwiftUI macOS app shellSources/PhotoLocSyncCore/— domain models, matching, and workflow orchestrationSources/PhotoLocSyncAdapters/— Timeline parsing, PhotoKit, local coordinate labeling, and security-scoped file accessTests/— importer, matcher, pipeline, and manual verification coverageConfiguration/— app bundle metadata used by Xcode and the local bundle-build scriptDocs/— migration notes for future iPhone and iPad shellsScripts/anonymize_timeline_fixture.py— helper used to anonymize a real Timeline export into a safe fixture
- Launch the macOS app from Xcode,
swift run PhotoLocSyncMac, or a generated.appbundle. - Import a Google Maps
location-history.jsonexport with the native file picker or drag-and-drop. - Grant Photos access.
- Review proposed location matches in list or map mode.
- Apply confirmed GPS metadata to Apple Photos.
make lint
make testmake lint builds source and test targets with -warnings-as-errors, and make test runs the test suite with the same warning-free requirement.
This repository also includes a tracked pre-commit hook at .githooks/pre-commit. Enable it in your clone with:
git config core.hooksPath .githooksUse the packaging script to build the SwiftPM app target and wrap it in a local macOS app bundle:
python3 Scripts/build_app_bundle.pyNotes:
- The default output is
.build/bundle/PhotoLocSyncMac.app. - Use
--configuration debugif you want a debug bundle instead of the default release build. - Add
--opento launch the packaged app as soon as the bundle has been written.
Use the watcher script to rebuild and relaunch the app whenever Swift or configuration files change:
python3 Scripts/hot_reload.pyNotes:
- The script watches
App/,Sources/,Configuration/, andPackage.swift. - On each successful rebuild it terminates the old app process and launches the new binary immediately.
- If a rebuild fails, the current app instance keeps running so you can fix the error and save again.
- To skip the initial test run:
python3 Scripts/hot_reload.py --no-testsOpen Package.swift in Xcode 26+ and run the PhotoLocSyncMac executable target.
Tests/PhotoLocSyncAdapterTests/Fixtures/location-history-anonymized.json was anonymized from a real Timeline export while preserving structure and ordering. Do not commit raw exports.
Follow Tests/PhotoLocSyncManualTests/MANUAL_TEST_GUIDE.md for end-to-end checks against a real Photos library.