iOS
Not built on the authors' machine.
npm run ios(prebuild, CocoaPods, Xcode, a simulator or an iPhone) was never run for this page. What was run:npx expo config(the config is valid and thereact-native-webgpuplugin resolves),npx expo prebuild --no-install --cleanin a throwaway copy of the template (it succeeded), andnpx expo export --platform ios(the JavaScript bundle builds). It is not known from our own runs whether the app launches, whether WebGPU draws in the simulator, or how fast it runs. The steps below are Expo's standard ones, checked against Expo SDK 58's docs and the template's config.
On iOS your game is an ordinary Expo app: expo run:ios generates the native project, builds it with Xcode, installs it and loads the game's JavaScript from Metro. The engine needs nothing from the Moonspell app to run there (What is and is not open).
Prerequisites
- A Mac with Xcode and an iOS simulator, or an iPhone with a cable.
expo run:iosonly runs on a Mac with Xcode installed. - CocoaPods (prebuild installs the pods).
- Node and npm, and a real
npm installin the game's folder. - For an iPhone: an Apple account in Xcode with a signing team, and a bundle identifier of your own (below). On iOS 16 and later the phone needs Developer Mode on (Expo: iOS developer mode).
Run it
npm install
npm run ios
npm run ios is expo run:ios. The first run generates ios/ (Expo's prebuild), installs the pods, compiles a debug build, installs it on a simulator and starts Metro. Later runs skip prebuild while ios/ exists. Arguments after -- go to expo run:ios:
npm run ios -- --device
--device lets you pick a connected iPhone or a simulator.
The template does not include expo-dev-client, so this is a plain debug build that loads from Metro, without the development launcher screen. Add expo-dev-client only if you want that launcher. React Native 0.88 always runs the New Architecture; there is no flag to set.
What app.json needs
The template's app.json:
{
"expo": {
"name": "Sunset Sprint",
"slug": "sunset-sprint",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "dark",
"backgroundColor": "#07070C",
"ios": { "bundleIdentifier": "com.rofi.crescentmoon.game" },
"android": { "package": "com.rofi.crescentmoon.game" },
"plugins": ["react-native-webgpu"],
"extra": { "genux": { "backgroundColor": "#07070C" } }
}
}
| Key | Why |
|---|---|
plugins: ["react-native-webgpu"] | react-native-webgpu's config plugin. On iOS it turns Metal API validation off in the Xcode scheme's Run action (the prebuild check showed enableGPUValidationMode = "1"). The library's docs say validation can raise false positives in Dawn that crash the app on simulators. Keep it |
ios.bundleIdentifier | Change com.rofi.crescentmoon.game to an id of your own before you build for a phone: Xcode makes the provisioning profile for this id |
orientation: "portrait" | Locks the app to portrait, the shape the engine's HUDs and controls are laid out for |
name | The app's name on the home screen |
extra.genux | Not read by the engine. Harmless in your build |
Minimum iOS version
The template does not set a deployment target, so prebuild uses Expo SDK 58's default: iOS 16.4 in the prebuild check. That is above what the engine's native libraries ask for (Skia's podspec says iOS 14.0; react-native-webgpu, MMKV and Reanimated follow React Native's minimum).
Deep links
The template sets no scheme, so your build opens no links. useLaunchRoomId() falls back to React Native's Linking, so if you add an Expo scheme to app.json, a link with ?room=CODE that opens the app drops the player into that room (Net). Not tested.
On an iPhone
npm run ios -- --device
Pick the connected phone. Set ios.bundleIdentifier to your own id first, so Xcode can make a provisioning profile for it, and turn on Developer Mode on the phone. For signing and local builds in general, follow Expo: local app development; this page does not repeat it.
react-native-webgpu's docs say the Metal validation false positives do not happen on physical devices, and recommend testing on hardware for real performance.
Release builds
At a pointer level only; none of this was run for the engine:
- Locally:
npx expo run:ios --configuration Releasemakes a release build, unsigned. Signing it for the App Store is covered by Expo: local app production. You can also openios/*.xcworkspacein Xcode and archive from there. - In the cloud: EAS Build (
eas build --platform ios), which handles signing credentials (Expo: EAS Build). Expo's development-build profiles expectexpo-dev-client, which the template does not include.
If reloads crash
A development build runs the upstream versions of Skia and react-native-audio-api. The Moonspell app carries its own fixes for two teardown problems in them (Skia releasing the Metal device too early, and react-native-audio-api calling a nil render block). With one game per app they should matter only when the JavaScript reloads, for example on save in development. If repeated reloads crash your build, suspect these first. Untested.
Different from the Moonspell app
| Inside the Moonspell app | Your Expo build | |
|---|---|---|
| How the game loads | The app loads the game's export into its own runtime | Your app is the game; Metro serves it in development |
| Exit button | Back to the app's games | exitToShell() does nothing; the template's App.tsx unmounts the game and shows its "Play" button |
| Paused off screen | Yes, while the app's feed keeps the game loaded but not shown | Never: the game is always active |
getLaunchUrl(), getShareUrl(), roomInviteUrl() | The app passes the links in | null |
| Live updates, App Clip, feed video | Yes | No |
| Game backend | The game's own Worker | GAME_BACKEND_URL is null in the template: leaderboards, presence and rooms stay on this device until you deploy the backend and set it |
On the phones the shell is the optional native module GenUXExpoModule; in your build requireOptionalNativeModule returns null and the engine runs on its own (Platform layer). All the differences between platforms are on Platform differences.
See also
- Android: the same for Android.
- Web: the fastest way to work on a game.
- Quickstart