Native apps (iOS & Android)
Diadem can be wrapped as a native Android and iOS app using Capacitor. The native app bundles the Diadem client and runs it in a system webview as a thin client: every request is sent to a remote Diadem instance.
Two flavours are possible from the same codebase:
- Generic app: on first launch the user is asked for the URL of the Diadem instance to connect to (the instance gate). They can change it later under Profile → Instance. This is meant for public builds that are hosted on app stores and the likes.
- Branded app: an instance URL is baked in at build time, so the gate and the instance switcher are hidden and the app always connects to that one instance. This is meant for when you want to build native Diadem yourself.
The map still renders with MapLibre GL JS inside the webview. (Native MapLibre rendering is not used.)
Requirements
Section titled “Requirements”- Everything from the normal installation (Node 22+, pnpm)
- JDK 21 and the Android SDK (Capacitor 7 builds against Java 21)
- A connected Android device with USB debugging, or an emulator
- iOS only: a Mac with Xcode, CocoaPods, and an Apple Developer account for App Store/TestFlight uploads
On macOS, make sure the full Xcode install is selected, not only the Command Line Tools:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developerxcodebuild -versionpod --versionA dev shell with the full Android toolchain (Node, JDK 21, Android SDK, Gradle)
is provided in flake.nix. Run native build commands inside it:
nix developOn other systems, install JDK 21 + the Android SDK yourself and set
ANDROID_HOME/JAVA_HOME.
How a native build works
Section titled “How a native build works”pnpm run build:native -> static client in build/pnpm run build:native:android -> build client and sync android/pnpm run build:native:ios -> build client and sync ios/After syncing, build the native project with Gradle, Xcode, or Capacitor.
Development builds
Section titled “Development builds”Build a debug APK and run it on a connected device.
-
Confirm the device is connected and enter the nix shell (if on NixOS):
Terminal window adb devicesnix develop -
Build the client and sync the Android platform:
Terminal window pnpm run build:native:android -
Build, install, and launch the debug APK:
Terminal window npx cap run androidOr build the APK manually and install it:
Terminal window cd android && ./gradlew assembleDebugadb install -r app/build/outputs/apk/debug/app-debug.apk
After changing client code, re-run pnpm run build:native:android before rebuilding.
iOS simulator builds
Section titled “iOS simulator builds”Build and run the app on an iOS simulator from a Mac with Xcode installed.
-
Build the client and sync the iOS platform:
Terminal window pnpm run build:native:ios -
Open the workspace in Xcode and run the App scheme on an iOS simulator:
Terminal window npx cap open ios -
Or build from the CLI against an installed simulator runtime:
Terminal window xcrun simctl list devices availablexcodebuild -workspace ios/App/App.xcworkspace \-scheme App \-configuration Debug \-destination 'platform=iOS Simulator,name=iPhone 15 Pro' \build
After changing client code, re-run pnpm run build:native:ios before rebuilding.
Test iOS deep links in the booted simulator with:
xcrun simctl openurl booted "diadem://pokemon/<id>"Testing deep links and auth
Section titled “Testing deep links and auth”-
Deep links use the
diadem://scheme. Simulate one with:Terminal window adb shell am start -a android.intent.action.VIEW -d "diadem://pokemon/<id>" -
Discord/OAuth login opens the system browser and returns to the app via
diadem://auth. This works against any Diadem instance, the requiredbearerauth support is built into the Diadem server.
Production builds
Section titled “Production builds”Android (Google Play)
Section titled “Android (Google Play)”Play requires a signed App Bundle (.aab).
-
Create a keystore (once) and keep it safe:
Terminal window keytool -genkey -v -keystore diadem.keystore -alias diadem \-keyalg RSA -keysize 2048 -validity 10000 -
Tell Gradle how to sign by creating
android/keystore.properties(do not commit it):storeFile=/absolute/path/to/diadem.keystorestorePassword=...keyAlias=diademkeyPassword=...and wiring it into
android/app/build.gradle(signingConfigs+buildTypes.release.signingConfig) per the Capacitor signing docs. -
Build the release bundle:
Terminal window pnpm run build:native:androidcd android && ./gradlew bundleRelease# -> android/app/build/outputs/bundle/release/app-release.aab
Upload the .aab to the Play Console.
iOS (App Store)
Section titled “iOS (App Store)”On a Mac:
pnpm run build:native:iosnpx cap open ios # opens XcodeSet the signing team and bundle id in Xcode, then Archive and upload to App
Store Connect. The committed project registers the diadem:// URL scheme for
OAuth/deep links and includes the location usage description required by iOS.
Branded builds and embedding an instance URL
Section titled “Branded builds and embedding an instance URL”To ship an app that always connects to one instance (hiding the gate and the
Profile → Instance switcher), set VITE_DIADEM_INSTANCE at build time:
VITE_DIADEM_INSTANCE=https://map.example.com pnpm run build:native:androidcd android && ./gradlew assembleDebug # or bundleReleaseOr put it in a .env file (gitignored) so every build picks it up:
VITE_DIADEM_INSTANCE=https://map.example.comWhen set:
- the first-run instance gate is skipped, the app connects straight to that URL,
- the Instance section in the profile menu is hidden,
- the URL cannot be changed at runtime.
Leave VITE_DIADEM_INSTANCE unset to build the generic app with the instance gate.
Customising app identity
Section titled “Customising app identity”App id, name, icon and splash live in the native projects:
- App id / name:
capacitor.config.ts(appId,appName). The default id isee.malt.diadem, change it before publishing your own app. - Icons / splash: replace the generated assets under
android/app/src/main/res/(andios/App/App/Assets.xcassets/on iOS), e.g. with@capacitor/assets. - Deep-link scheme: the
diadem://intent filter is inandroid/app/src/main/AndroidManifest.xml; the iOS URL scheme is inios/App/App/Info.plist.