iOS
Meratyn runs as a native iOS app built from the same React codebase via Tauri v2 mobile — no separate app. The webview UI (WKWebView) is bundled in the app, so the shell works offline; map tiles and the heavier engines are fetched on demand (same as the desktop and Android builds).
Status: scaffolding. The iOS config, privacy plist, and CI build/signing workflow are in place, but no iOS build has been shipped yet. Local builds still require a Mac because iOS cannot be cross-compiled from Linux.
What works on iOS vs desktop
Same split as Android. The iOS build ships the full map workspace, Add Data, the Vector tools (Turf.js / in-browser GeoPandas via Pyodide), the SQL Workspace (DuckDB-WASM and PGlite/PostGIS), the Python Console (Pyodide), geocoding, statistics, the AI assistant, story maps, and plugins.
iOS has no Python sidecar or local helper binaries and its sandbox forbids
spawning subprocesses. Browser-capable processing runs on the device where the
operation and input format are supported: Whitebox and raster tools use their
WASM engines, and browser conversion formats remain available. Sidecar-only
tools, native-file paths, and the local PostgreSQL/Martin source stay hidden on
mobile. These limits are gated by a user-agent isMobile() check (which
already matches iPhone/iPad), so an unavailable path does not appear and then
fail.
Location permission (required)
This is the one place iOS differs sharply from Android. Android’s geolocation
plugin declares the runtime permission and the OS shows a generic dialog; iOS
terminates the app the instant it requests location if no usage-description
string is present. Meratyn supplies it in
src-tauri/Info.ios.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Meratyn uses your location to center the map, capture GPS points during Field Collection, and record GPS tracks.</string>
Tauri merges Info.ios.plist into the generated
gen/apple/mappview_iOS/Info.plist at build time. gen/apple is git-ignored, so
this file is the durable home for the string — the same reason Android’s manifest
permissions come from the plugin rather than a hand-edited, regenerated manifest.
It covers all three location consumers: Field Collection, GPS Tracking, and the
Controls → GeoLocate map control. After a build, confirm the key survived the
merge (see Build below).
Toolchain setup (one time)
You need a Mac with Xcode (from the App Store; open it once to accept the license and install the iOS platform), the command-line tools, CocoaPods, and the Rust iOS targets.
xcode-select --install # command-line tools (if not already)
sudo xcodebuild -license accept
brew install cocoapods # or `gem install cocoapods`
# Rust device + simulator targets (install rustup first if needed)
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
aarch64-apple-ios is real devices and signed release .ipa archives;
aarch64-apple-ios-sim is the simulator on Apple Silicon Macs.
Build
cd apps/mappview
bunx --no-install tauri ios init # generate src-tauri/gen/apple (once)
bunx --no-install tauri ios dev # run in the simulator / on a tethered device
bunx --no-install tauri ios build # release archive → signed .ipa (needs signing, below)
-
gen/appleis generated (git-ignored) and regenerated on demand.initalso appliestauri.ios.conf.json(bundle id, drops the Python backend).Info.ios.plistis merged later bytauri ios buildortauri ios dev. -
The app is named Meratyn on iOS (the desktop build is “Meratyn Desktop”) and uses the bundle id
com.mapptech.mappview, both set viasrc-tauri/tauri.ios.conf.json— the same override pattern and reasoning as Android (identifierintauri.conf.jsonstayscom.mapptech.mappviewso desktop upgrades keep using the existing data and configuration namespaces). -
The durable iOS deployment target is iOS 15.0, configured as
bundle.iOS.minimumSystemVersionintauri.ios.conf.json. The CI workflow verifies that Tauri carries it into both the generated Xcode project and a signed IPA before an upload can proceed. -
Verify the location string landed after a build:
/usr/libexec/PlistBuddy -c 'Print :NSLocationWhenInUseUsageDescription' \ src-tauri/gen/apple/mappview_iOS/Info.plist
Signing
Every build that runs on a real device must be signed — there is no debug-keystore shortcut like Android’s. You need:
- An Apple Developer Program membership ($99/year).
- A signing certificate (Apple Development for device testing; the
distribution certificate, when available, for distribution builds),
exported from Keychain as a
.p12. - A provisioning profile for the
com.mapptech.mappviewapp id. - Your Team ID (Developer portal → Membership details).
Opening gen/apple/mappview.xcodeproj in Xcode and enabling “Automatically
manage signing” with the Meratyn team is the simplest local path.
The iOS GitHub Actions workflow also runs on releases or manually. Without
signing secrets it performs a real-device Rust compile check. With the complete
secret set it creates an unsigned archive, stamps a unique build number,
exports it under explicit manual signing, verifies the shipped bundle
identifier/signature/plist, and uploads the .ipa:
APPLE_IOS_CERTIFICATE_BASE64— Apple Distribution.p12, base64 encodedAPPLE_IOS_CERTIFICATE_PASSWORDAPPLE_IOS_PROVISIONING_PROFILE_BASE64— manually managed profile forcom.mapptech.mappview, base64 encodedAPPLE_TEAM_ID
The iOS certificate secrets are intentionally separate from the macOS Developer ID credentials; those certificate types are not interchangeable.
Install / test
- Simulator:
bunx --no-install tauri ios devand pick a simulator, or open the Xcode project and Run. No paid account needed for the simulator. - Your own device: tether it, open
gen/apple/mappview.xcodeprojin Xcode, select the device, and Run (a free Apple ID allows 7-day device signing). - Testers: hand them the signed
.ipato sideload via Xcode (Devices & Simulators window), Finder, or Apple Configurator; or they can opengen/apple/mappview.xcodeprojin Xcode themselves and Run on their own device.
Known limitations / follow-ups
Most mirror Android:
- Local-file sources (MBTiles, local rasters, project files) assume real filesystem paths; iOS hands apps sandboxed URLs via the document picker, so those flows need adapting before they work natively.
- The Download Offline Area tool relies on a service worker, which the Tauri builds don’t use — it’s a PWA feature. Native offline basemap caching is future work.
- Earth Engine OAuth uses a desktop loopback/multi-window flow; a mobile deep-link redirect (an iOS URL scheme / universal link) is future work.
- iPadOS multitasking (Split View / Stage Manager) hasn’t been tuned; the responsive layout should adapt, but verify on a real iPad before release.