Local-first SwiftUI + SwiftData hiking app for iPhone — GPX import/export, live recording with OpenStreetMap trail matching, offline map tiles, photos pinned to the trail, iCloud sync, a Home Screen widget, and reviewed community trails. No account: your hikes stay on your device unless you publish one.
Swift
0
784 commits
updated Sep 29, 2026
OpenHikes is a local-first SwiftUI and SwiftData trail viewer for iPhone, with a companion app for Apple Watch. It imports GPX tracks, records live hikes, displays them on a MapKit map, provides route statistics and an interactive elevation profile, and keeps selected map areas available offline.
That is local-first with one deliberate exception. There is no OpenHikes account and no server holding your hikes: everything lives on the device, and what syncs travels through the hiker's own private iCloud database. The exception is sharing a hike, which publishes it to a public CloudKit database other people browse. OpenHikes/PrivacyInfo.xcprivacy and the privacy policy describe exactly what that sends.
.gpx opens straight into the app from Files, AirDrop or any share sheet — or several at once from Import GPX — and a file holding a multi-day trip or a region's walks becomes one hike per track, after a list of them you can untick. Each arrives with track metadata, route statistics — overall and moving-time average speed side by side, so a long lunch stop does not read as a slow walk — elevation-chart scrubbing, route styling and direction chevrons. The Share button hands a hike back out as GPX 1.1.27A266a). The phone targets
deploy to iOS 27.0, because the trail maker's stops reorder with iOS 27's
reorderable(); the two local packages, OpenHikesShared and
OpenHikesData, still declare iOS 26. The
CodeQL workflow is the one exception and still builds on Xcode 26.6, because
CodeQL's Swift extractor does not read Swift 6.4 yet — it builds without the
maker's drag, which is the one iOS 27 API in the app.TARGETED_DEVICE_FAMILY = 1; OpenHikesWatch sets 4 and deploys to
watchOS 26.0. The watch app is embedded in the phone app, so building the
OpenHikes scheme builds it too.OpenStreetMap is the keyless default and Apple Maps needs no key either. Stadia and Thunderforest require build-time API keys and a paid subscription with each vendor, whose terms forbid using them free of charge in a shipping app — in OpenHikes they sit behind a monthly subscription, OpenHikes Pro, which is what pays for them, along with saving a whole route's Stadia map for offline use — Thunderforest's licence reserves pre-caching for a plan this app is not on. Everything else in the app, the community feature included, is free. A build without keys shows them locked, and OpenStreetMap keeps working.
Open OpenHikes.xcodeproj and set your development team for OpenHikes, OpenWidgetExtension and OpenHikesWatch.
Enable WeatherKit for the app's App ID in Certificates, Identifiers & Profiles, in both App Services and App Capabilities, then refresh its signing assets. The capability and entitlement are checked in, but Apple still returns HTTP 401 until the App ID itself is enabled.
If your team cannot use group.tappium.com.OpenHikes, replace it in both entitlement files and in SharedStore.appGroupID.
HealthKit needs no portal step in the ordinary case: the capability and both usage strings are checked in, and the App ID picks it up when Xcode refreshes signing assets. Enable it by hand in App Capabilities if signing refuses. The app only ever writes a finished hike into the hiker's own store — HealthKitWorkoutWriter asks for share types and no read types — and the switch is off until they turn it on.
Time Sensitive Notifications needs no portal step in the ordinary case either, and the entitlement is checked in. Without it the two warnings the app can send — a severe-weather alert and leaving the trail — are delivered at the ordinary level, which any Focus mode silences; nothing reports that, so the symptom is a warning a hiker never sees.
iCloud sync needs a CloudKit container. Xcode creates iCloud.tappium.com.OpenHikes on the first signed build; to use another identifier, replace it in OpenHikes/OpenHikes.entitlements and in CloudSyncCoordinator.containerIdentifier. SwiftData's mirroring creates the development schema from the model on first run.
Optionally enable Stadia or Thunderforest:
cp Secrets.example.plist OpenHikes/Secrets.plist
Add your keys to the copied file. OpenHikes/Secrets.plist is gitignored and must never be committed; unavailable providers stay disabled in Settings.
Build and run. OpenHikes.storekit at the repository root describes the OpenHikes Pro subscription and the shared scheme already points its Run action at it, so a local build has a working paywall with no Apple account involved.
Shipping the subscription for real additionally needs a matching auto-renewable subscription in App Store Connect and an active Paid Apps agreement; .github/copilot-instructions.md carries the exact contract, including the product ID that can never change.
Launch OpenHikes on a booted iOS Simulator, tap the record button (VoiceOver's Record a hike: the lower of the two glyph buttons on the map's leading edge, under the one that draws a trail), which starts a recording and opens it, and replay the bundled Thumsee route:
Scripts/simulate-hike.sh start # ~1.7 km accelerated preview
Scripts/simulate-hike.sh --full --speed 4 # the complete 9.3 km route
Scripts/simulate-hike.sh stop # stop and clear location playback
--help selects a simulator, another GPX file, playback speed, update interval or point count.
# Boot the simulator first — the test commands below need it awake
xcrun simctl boot "iPhone 18 Pro" || true
xcrun simctl bootstatus "iPhone 18 Pro" -b
# Build the app, its embedded widget and the watch app. The watch target is a
# dependency of the app, so this compiles it for watchOS as well — there is no
# separate build to remember, and no way to break it without breaking this.
xcodebuild build -project OpenHikes.xcodeproj -scheme OpenHikes \
-destination 'platform=iOS Simulator,name=iPhone 18 Pro'
# The watch app on its own, which is the faster loop while working on it
xcodebuild build -project OpenHikes.xcodeproj -scheme OpenHikesWatch \
-destination 'generic/platform=watchOS Simulator'
# Unit and integration tests, app and widget
xcodebuild test -project OpenHikes.xcodeproj -scheme OpenHikes \
-destination 'platform=iOS Simulator,name=iPhone 18 Pro' \
-only-testing:OpenHikesTests -only-testing:OpenWidgetTests
# The standalone shared-package suite
swift test --package-path OpenHikesShared
# The data package's suite: the models and their logic, on the macOS host
swift test --package-path OpenHikesData
# Simulator UI automation, across four simulator clones; --serial for one,
# and --list shows the available tests. A second run on the same machine needs
# its own of both: --device <name|udid> and --derived-data <path>
Scripts/run-ui-tests.sh --all
# Strict SwiftLint, the same one CI runs; --fix applies what it can correct
Scripts/lint.sh
# The shell scripts' own tests, against stubbed xcrun/xcodebuild — nothing is
# built and no simulator is touched. CI runs this one too.
Scripts/run-script-tests.sh
Against a cold simulator, xcodebuild test fails with "The test runner hung before establishing connection" after several minutes without a single test having reported — which is why the boot is the first line above rather than an optional one.
These commands name the same device CI does. iPhone 18 Pro is the Pro model an iOS 27 runtime ships, and .github/workflows/ci.yml names it too, on a runner image that carries the same Xcode 27.0 build as a current local install. On an Xcode 26 install there is no such simulator — name whichever device your own runtimes do have.
Unit and integration tests use Swift Testing; OpenHikesUITests uses XCUITest, because Apple's UI automation and launch metrics are not available through Swift Testing.
brew install xcbeautify periphery xcode-build-server installs the optional tooling. None of it is required: each tool is used if present and skipped if not. periphery has to be the version in .periphery-version or newer — run it through Scripts/periphery.sh, which checks that first, because an older Periphery reads none of .periphery.yml and reports a clean scan anyway. xcode-build-server is per-machine — run xcode-build-server config -project OpenHikes.xcodeproj -scheme OpenHikes locally, and again after adding or renaming a target.
CI runs strict SwiftLint, the shell scripts' own smoke tests, the shared package suite in both debug and release, the app and widget unit tests with a coverage floor, warning-free debug and release builds, an unsigned device archive, the concurrency suites under Thread Sanitizer, and both accessibility UI classes. CodeQL and a dependency review run beside it. The functional UI automation stays out, because it leans on real gestures and timing-sensitive waits that a shared runner makes slow and flaky — run it locally before a change that touches recording, the map or render isolation.
AGENTS.md and .github/copilot-instructions.md — the architecture, the conventions, the energy policies, and the decisions already settled.CONTRIBUTING.md — the short version for a first change.SECURITY.md — how to report a vulnerability privately, and what the public community database grants to whom.Screenshots/README.md — capturing the App Store screenshot set, and the route and photographs the frames are built from.APP_REVIEW.md — the notes the App Store listing carries for its reviewer, including why the app asks for background location.Following Apple's Food Truck and Backyard Birds samples, app source is organized by product domain rather than by generic Managers, Models and Views layers. OpenHikesModel is the composition root injected into the SwiftUI environment; feature-specific state and behaviour stay in their domain folders.
| Path | Purpose |
|---|---|
OpenHikes/App/ | App entry point, shared app model, configuration, deep-link routing, root navigation. |
OpenHikes/Hikes/ | GPX import and export, drawing a trail on the map, the places along a trail, the surface and difficulty breakdowns, walks along a saved trail and their history, hike screens. The model itself is in OpenHikesData/. |
OpenHikes/Recording/ | Live recording, recovery journal, sensors, trail matching, recording UI. |
OpenHikes/Map/ | MapKit bridge, map state, search, location tracking, map rendering. |
OpenHikes/Tiles/ | Tile provider policy, cache, auto-save, offline downloads, overlay rendering. |
OpenHikes/Community/ | Publishing a hike to the public database, browsing and searching what other people published, the waymarked routes fetched from OpenStreetMap that fill the same list, the map's lines and pins for both, importing one, offering photographs to a trail that is already public, reviewing what is waiting, reporting and blocking. |
OpenHikes/Photos/ | Capture and import, library discovery and time-to-place matching, the file store, trail anchoring, gallery, viewer and map pins. |
OpenHikes/Health/ | Writing a finished hike into the hiker's own Health store, behind a switch and behind a seam that keeps HealthKit out of the tests. |
OpenHikes/Sync/ | iCloud sync status and control, and the settings key-value mirror. |
OpenHikes/Weather/ | WeatherKit polling, the badge over the map and its detail sheet, unit formatting, and Apple Weather attribution. |
OpenHikes/Purchases/ | The Pro entitlement and its StoreKit state, the paywall, and the subscription terms and links. |
OpenHikes/Settings/ | User-facing app, recording, map and storage settings. |
OpenHikes/Reminders/ | Noticing that a paused hike has started moving again, or that a running one has stopped, and saying so once. |
OpenHikes/LiveActivity/ | When a Lock Screen activity starts, updates and ends, behind a seam that keeps ActivityKit out of the tests. |
OpenHikes/Intents/ | App Intents for controlling and querying a recording, the Siri and Spotlight shortcuts they are offered through, and the seam they perform behind. |
OpenHikes/General/ | Cross-domain extensions and diagnostics. |
OpenHikes/SimulatedLocations/ | The bundled GPX routes the simulated hike and the screenshot capture play back. |
OpenHikes/Watch/ | The phone's half of the watch link: sending the hiker's trails and one trail's geometry, and keeping the walks the watch recorded. |
OpenHikesShared/ | Domain-foldered local Swift package shared by the app, the widget and the watch. |
OpenHikesData/ | What a hike is: the SwiftData models, the schema, the route index and the logic over them, as a local package the app builds on. |
OpenWidget/ | iOS Home Screen widget and the Live Activity's Lock Screen and Dynamic Island views. |
OpenHikesWatch/ | The watchOS app: the link to the phone, the trail being followed, and recording a hike on the watch alone. |
OpenHikesWatchWidgets/ | The watch's complication and Smart Stack widget, drawn from the glance the watch's recorder writes. |
OpenHikesTests/, OpenWidgetTests/ | App-hosted tests mirroring the app's domain folders. |
OpenHikesUITests/ | Simulator UI automation, location spoofing, launch metrics. |
Scripts/ | The gates and tools a contributor runs by hand: lint, the UI-test runner, the simulated hike, the App Store screenshot capture and its photo stamper, and the checks CI runs beside them. |
Screenshots/ | The App Store screenshot set: what each frame has to say, how it is captured, and where its photographs come from. |
docs/ | The published GitHub Pages site — the privacy, terms and support pages the App Store listing links. Not a documentation folder. |
ci_scripts/ | Xcode Cloud hooks, run automatically by name. |
OpenHikes is released under the MIT License.
That covers the source in this repository only. Map data and map tiles are not ours to license: OpenStreetMap data is © OpenStreetMap contributors and is published under the Open Database License, and the Stadia Maps and Thunderforest styles are used under their own terms. The app displays the credit each provider requires, which OpenHikes/Tiles/TileAttribution.swift is responsible for and its tests enforce. A fork that changes tile providers, or that redistributes cached tiles, takes on those obligations itself.
Dependencies: swift-algorithms, swift-collections and swift-async-algorithms are Apache-2.0 licensed and ship inside the app; swift-numerics, also Apache-2.0, is linked directly only by the test targets, and reaches the app solely as a dependency of swift-algorithms; SwiftLint is a build-time plugin and is not linked into the binary.
For feedback and suggestions, email zsombor.rajki@gmail.com or visit the OpenHikes project on GitHub.
Swift
97.9%
Shell
2.1%
Local-first SwiftUI + SwiftData hiking app for iPhone — GPX import/export, live recording with OpenStreetMap trail matching, offline map tiles, photos pinned to the trail, iCloud sync, a Home Screen widget, and reviewed community trails. No account: your hikes stay on your device unless you publish one.
Swift
0
784 commits
updated Sep 29, 2026
OpenHikes is a local-first SwiftUI and SwiftData trail viewer for iPhone, with a companion app for Apple Watch. It imports GPX tracks, records live hikes, displays them on a MapKit map, provides route statistics and an interactive elevation profile, and keeps selected map areas available offline.
That is local-first with one deliberate exception. There is no OpenHikes account and no server holding your hikes: everything lives on the device, and what syncs travels through the hiker's own private iCloud database. The exception is sharing a hike, which publishes it to a public CloudKit database other people browse. OpenHikes/PrivacyInfo.xcprivacy and the privacy policy describe exactly what that sends.
.gpx opens straight into the app from Files, AirDrop or any share sheet — or several at once from Import GPX — and a file holding a multi-day trip or a region's walks becomes one hike per track, after a list of them you can untick. Each arrives with track metadata, route statistics — overall and moving-time average speed side by side, so a long lunch stop does not read as a slow walk — elevation-chart scrubbing, route styling and direction chevrons. The Share button hands a hike back out as GPX 1.1.27A266a). The phone targets
deploy to iOS 27.0, because the trail maker's stops reorder with iOS 27's
reorderable(); the two local packages, OpenHikesShared and
OpenHikesData, still declare iOS 26. The
CodeQL workflow is the one exception and still builds on Xcode 26.6, because
CodeQL's Swift extractor does not read Swift 6.4 yet — it builds without the
maker's drag, which is the one iOS 27 API in the app.TARGETED_DEVICE_FAMILY = 1; OpenHikesWatch sets 4 and deploys to
watchOS 26.0. The watch app is embedded in the phone app, so building the
OpenHikes scheme builds it too.OpenStreetMap is the keyless default and Apple Maps needs no key either. Stadia and Thunderforest require build-time API keys and a paid subscription with each vendor, whose terms forbid using them free of charge in a shipping app — in OpenHikes they sit behind a monthly subscription, OpenHikes Pro, which is what pays for them, along with saving a whole route's Stadia map for offline use — Thunderforest's licence reserves pre-caching for a plan this app is not on. Everything else in the app, the community feature included, is free. A build without keys shows them locked, and OpenStreetMap keeps working.
Open OpenHikes.xcodeproj and set your development team for OpenHikes, OpenWidgetExtension and OpenHikesWatch.
Enable WeatherKit for the app's App ID in Certificates, Identifiers & Profiles, in both App Services and App Capabilities, then refresh its signing assets. The capability and entitlement are checked in, but Apple still returns HTTP 401 until the App ID itself is enabled.
If your team cannot use group.tappium.com.OpenHikes, replace it in both entitlement files and in SharedStore.appGroupID.
HealthKit needs no portal step in the ordinary case: the capability and both usage strings are checked in, and the App ID picks it up when Xcode refreshes signing assets. Enable it by hand in App Capabilities if signing refuses. The app only ever writes a finished hike into the hiker's own store — HealthKitWorkoutWriter asks for share types and no read types — and the switch is off until they turn it on.
Time Sensitive Notifications needs no portal step in the ordinary case either, and the entitlement is checked in. Without it the two warnings the app can send — a severe-weather alert and leaving the trail — are delivered at the ordinary level, which any Focus mode silences; nothing reports that, so the symptom is a warning a hiker never sees.
iCloud sync needs a CloudKit container. Xcode creates iCloud.tappium.com.OpenHikes on the first signed build; to use another identifier, replace it in OpenHikes/OpenHikes.entitlements and in CloudSyncCoordinator.containerIdentifier. SwiftData's mirroring creates the development schema from the model on first run.
Optionally enable Stadia or Thunderforest:
cp Secrets.example.plist OpenHikes/Secrets.plist
Add your keys to the copied file. OpenHikes/Secrets.plist is gitignored and must never be committed; unavailable providers stay disabled in Settings.
Build and run. OpenHikes.storekit at the repository root describes the OpenHikes Pro subscription and the shared scheme already points its Run action at it, so a local build has a working paywall with no Apple account involved.
Shipping the subscription for real additionally needs a matching auto-renewable subscription in App Store Connect and an active Paid Apps agreement; .github/copilot-instructions.md carries the exact contract, including the product ID that can never change.
Launch OpenHikes on a booted iOS Simulator, tap the record button (VoiceOver's Record a hike: the lower of the two glyph buttons on the map's leading edge, under the one that draws a trail), which starts a recording and opens it, and replay the bundled Thumsee route:
Scripts/simulate-hike.sh start # ~1.7 km accelerated preview
Scripts/simulate-hike.sh --full --speed 4 # the complete 9.3 km route
Scripts/simulate-hike.sh stop # stop and clear location playback
--help selects a simulator, another GPX file, playback speed, update interval or point count.
# Boot the simulator first — the test commands below need it awake
xcrun simctl boot "iPhone 18 Pro" || true
xcrun simctl bootstatus "iPhone 18 Pro" -b
# Build the app, its embedded widget and the watch app. The watch target is a
# dependency of the app, so this compiles it for watchOS as well — there is no
# separate build to remember, and no way to break it without breaking this.
xcodebuild build -project OpenHikes.xcodeproj -scheme OpenHikes \
-destination 'platform=iOS Simulator,name=iPhone 18 Pro'
# The watch app on its own, which is the faster loop while working on it
xcodebuild build -project OpenHikes.xcodeproj -scheme OpenHikesWatch \
-destination 'generic/platform=watchOS Simulator'
# Unit and integration tests, app and widget
xcodebuild test -project OpenHikes.xcodeproj -scheme OpenHikes \
-destination 'platform=iOS Simulator,name=iPhone 18 Pro' \
-only-testing:OpenHikesTests -only-testing:OpenWidgetTests
# The standalone shared-package suite
swift test --package-path OpenHikesShared
# The data package's suite: the models and their logic, on the macOS host
swift test --package-path OpenHikesData
# Simulator UI automation, across four simulator clones; --serial for one,
# and --list shows the available tests. A second run on the same machine needs
# its own of both: --device <name|udid> and --derived-data <path>
Scripts/run-ui-tests.sh --all
# Strict SwiftLint, the same one CI runs; --fix applies what it can correct
Scripts/lint.sh
# The shell scripts' own tests, against stubbed xcrun/xcodebuild — nothing is
# built and no simulator is touched. CI runs this one too.
Scripts/run-script-tests.sh
Against a cold simulator, xcodebuild test fails with "The test runner hung before establishing connection" after several minutes without a single test having reported — which is why the boot is the first line above rather than an optional one.
These commands name the same device CI does. iPhone 18 Pro is the Pro model an iOS 27 runtime ships, and .github/workflows/ci.yml names it too, on a runner image that carries the same Xcode 27.0 build as a current local install. On an Xcode 26 install there is no such simulator — name whichever device your own runtimes do have.
Unit and integration tests use Swift Testing; OpenHikesUITests uses XCUITest, because Apple's UI automation and launch metrics are not available through Swift Testing.
brew install xcbeautify periphery xcode-build-server installs the optional tooling. None of it is required: each tool is used if present and skipped if not. periphery has to be the version in .periphery-version or newer — run it through Scripts/periphery.sh, which checks that first, because an older Periphery reads none of .periphery.yml and reports a clean scan anyway. xcode-build-server is per-machine — run xcode-build-server config -project OpenHikes.xcodeproj -scheme OpenHikes locally, and again after adding or renaming a target.
CI runs strict SwiftLint, the shell scripts' own smoke tests, the shared package suite in both debug and release, the app and widget unit tests with a coverage floor, warning-free debug and release builds, an unsigned device archive, the concurrency suites under Thread Sanitizer, and both accessibility UI classes. CodeQL and a dependency review run beside it. The functional UI automation stays out, because it leans on real gestures and timing-sensitive waits that a shared runner makes slow and flaky — run it locally before a change that touches recording, the map or render isolation.
AGENTS.md and .github/copilot-instructions.md — the architecture, the conventions, the energy policies, and the decisions already settled.CONTRIBUTING.md — the short version for a first change.SECURITY.md — how to report a vulnerability privately, and what the public community database grants to whom.Screenshots/README.md — capturing the App Store screenshot set, and the route and photographs the frames are built from.APP_REVIEW.md — the notes the App Store listing carries for its reviewer, including why the app asks for background location.Following Apple's Food Truck and Backyard Birds samples, app source is organized by product domain rather than by generic Managers, Models and Views layers. OpenHikesModel is the composition root injected into the SwiftUI environment; feature-specific state and behaviour stay in their domain folders.
| Path | Purpose |
|---|---|
OpenHikes/App/ | App entry point, shared app model, configuration, deep-link routing, root navigation. |
OpenHikes/Hikes/ | GPX import and export, drawing a trail on the map, the places along a trail, the surface and difficulty breakdowns, walks along a saved trail and their history, hike screens. The model itself is in OpenHikesData/. |
OpenHikes/Recording/ | Live recording, recovery journal, sensors, trail matching, recording UI. |
OpenHikes/Map/ | MapKit bridge, map state, search, location tracking, map rendering. |
OpenHikes/Tiles/ | Tile provider policy, cache, auto-save, offline downloads, overlay rendering. |
OpenHikes/Community/ | Publishing a hike to the public database, browsing and searching what other people published, the waymarked routes fetched from OpenStreetMap that fill the same list, the map's lines and pins for both, importing one, offering photographs to a trail that is already public, reviewing what is waiting, reporting and blocking. |
OpenHikes/Photos/ | Capture and import, library discovery and time-to-place matching, the file store, trail anchoring, gallery, viewer and map pins. |
OpenHikes/Health/ | Writing a finished hike into the hiker's own Health store, behind a switch and behind a seam that keeps HealthKit out of the tests. |
OpenHikes/Sync/ | iCloud sync status and control, and the settings key-value mirror. |
OpenHikes/Weather/ | WeatherKit polling, the badge over the map and its detail sheet, unit formatting, and Apple Weather attribution. |
OpenHikes/Purchases/ | The Pro entitlement and its StoreKit state, the paywall, and the subscription terms and links. |
OpenHikes/Settings/ | User-facing app, recording, map and storage settings. |
OpenHikes/Reminders/ | Noticing that a paused hike has started moving again, or that a running one has stopped, and saying so once. |
OpenHikes/LiveActivity/ | When a Lock Screen activity starts, updates and ends, behind a seam that keeps ActivityKit out of the tests. |
OpenHikes/Intents/ | App Intents for controlling and querying a recording, the Siri and Spotlight shortcuts they are offered through, and the seam they perform behind. |
OpenHikes/General/ | Cross-domain extensions and diagnostics. |
OpenHikes/SimulatedLocations/ | The bundled GPX routes the simulated hike and the screenshot capture play back. |
OpenHikes/Watch/ | The phone's half of the watch link: sending the hiker's trails and one trail's geometry, and keeping the walks the watch recorded. |
OpenHikesShared/ | Domain-foldered local Swift package shared by the app, the widget and the watch. |
OpenHikesData/ | What a hike is: the SwiftData models, the schema, the route index and the logic over them, as a local package the app builds on. |
OpenWidget/ | iOS Home Screen widget and the Live Activity's Lock Screen and Dynamic Island views. |
OpenHikesWatch/ | The watchOS app: the link to the phone, the trail being followed, and recording a hike on the watch alone. |
OpenHikesWatchWidgets/ | The watch's complication and Smart Stack widget, drawn from the glance the watch's recorder writes. |
OpenHikesTests/, OpenWidgetTests/ | App-hosted tests mirroring the app's domain folders. |
OpenHikesUITests/ | Simulator UI automation, location spoofing, launch metrics. |
Scripts/ | The gates and tools a contributor runs by hand: lint, the UI-test runner, the simulated hike, the App Store screenshot capture and its photo stamper, and the checks CI runs beside them. |
Screenshots/ | The App Store screenshot set: what each frame has to say, how it is captured, and where its photographs come from. |
docs/ | The published GitHub Pages site — the privacy, terms and support pages the App Store listing links. Not a documentation folder. |
ci_scripts/ | Xcode Cloud hooks, run automatically by name. |
OpenHikes is released under the MIT License.
That covers the source in this repository only. Map data and map tiles are not ours to license: OpenStreetMap data is © OpenStreetMap contributors and is published under the Open Database License, and the Stadia Maps and Thunderforest styles are used under their own terms. The app displays the credit each provider requires, which OpenHikes/Tiles/TileAttribution.swift is responsible for and its tests enforce. A fork that changes tile providers, or that redistributes cached tiles, takes on those obligations itself.
Dependencies: swift-algorithms, swift-collections and swift-async-algorithms are Apache-2.0 licensed and ship inside the app; swift-numerics, also Apache-2.0, is linked directly only by the test targets, and reaches the app solely as a dependency of swift-algorithms; SwiftLint is a build-time plugin and is not linked into the binary.
For feedback and suggestions, email zsombor.rajki@gmail.com or visit the OpenHikes project on GitHub.
Swift
97.9%
Shell
2.1%