From 7fb87fbb961fc2a082392aa90bb818ae82769670 Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Wed, 7 Oct 2026 13:00:58 +0200 Subject: [PATCH] run on ios --- index.ts | 22 ++++++++++++++++-- .../app.json" | 5 ++-- template/devenv.nix | 23 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/index.ts b/index.ts index 2703dd2..8613f3d 100644 --- a/index.ts +++ b/index.ts @@ -37,6 +37,22 @@ const project = await p.group( ] }) , + bundleid: async ({ results }) => { + if (results.mobile !== "expo") return "" + // Valid as both an iOS bundle identifier and an Android package name. + const fallback = `com.${String(results.name).toLowerCase().replace(/[^a-z0-9]/g, "").replace(/^[0-9]+/, "") || "app"}.mobile` + return await p.text({ + message: "What bundle identifier should the mobile app use?", + placeholder: fallback, + defaultValue: fallback, + validate: (value) => { + if (value && !/^[a-zA-Z][a-zA-Z0-9]*(\.[a-zA-Z][a-zA-Z0-9]*)+$/.test(value)) { + return "Use reverse-DNS segments of letters and digits, e.g. com.example.app" + } + return undefined + }, + }) + }, goprefix: () => p.text({ message: "What would you like to use as a go package prefix?", @@ -65,11 +81,13 @@ const render = createRenderer(z.object({ name: z.string(), goprefix: z.string(), frontend: z.string(), - mobile: z.string() + mobile: z.string(), + bundleid: z.string() }) })) const destDir = path.join("./", project.name); -render(destDir, { project }, { reverseMap: true }) +// `results` in the bundleid prompt makes group() infer it as unknown. +render(destDir, { project: { ...project, bundleid: project.bundleid as string } }, { reverseMap: true }) const s = p.spinner(); if (project.installDeps) { diff --git "a/template/apps/<@if(eq(context.project.mobile,\"expo\"))>mobile/app.json" "b/template/apps/<@if(eq(context.project.mobile,\"expo\"))>mobile/app.json" index f83a317..442c385 100644 --- "a/template/apps/<@if(eq(context.project.mobile,\"expo\"))>mobile/app.json" +++ "b/template/apps/<@if(eq(context.project.mobile,\"expo\"))>mobile/app.json" @@ -8,7 +8,8 @@ "scheme": "mobile", "userInterfaceStyle": "automatic", "ios": { - "icon": "./assets/expo.icon" + "icon": "./assets/expo.icon", + "bundleIdentifier": "<@var(context.project.bundleid)>" }, "android": { "adaptiveIcon": { @@ -18,7 +19,7 @@ "monochromeImage": "./assets/images/android-icon-monochrome.png" }, "predictiveBackGestureEnabled": false, - "package": "com.gregorl.mobile" + "package": "<@var(context.project.bundleid)>" }, "web": { "output": "static", diff --git a/template/devenv.nix b/template/devenv.nix index 3440424..39ff279 100644 --- a/template/devenv.nix +++ b/template/devenv.nix @@ -40,6 +40,23 @@ echo no | avdmanager create avd -n "Pixel_5_API34" -k "system-images;android-34;google_apis_playstore;${if pkgs.stdenv.hostPlatform.isAarch64 then "arm64-v8a" else "x86_64"}" -d "pixel_5" fi ''; + # macOS only: run on the iOS simulator when Xcode with a simulator is installed. + scripts.run-ios = lib.mkIf pkgs.stdenv.isDarwin { + exec = '' + # devenv points DEVELOPER_DIR at the Nix SDK, which xcode-select would echo back. + dev_dir="$(/usr/bin/env -u DEVELOPER_DIR /usr/bin/xcode-select -p 2>/dev/null)" + [ -x "$dev_dir/usr/bin/simctl" ] || dev_dir=/Applications/Xcode.app/Contents/Developer + if ! DEVELOPER_DIR="$dev_dir" /usr/bin/xcrun simctl list devices available 2>/dev/null | grep -q iPhone; then + echo "No Xcode iOS simulator found, skipping iOS." + exit 0 + fi + # devenv's Nix Apple SDK, xcrun and compiler wrappers break xcodebuild, so build in a clean env. + exec /usr/bin/env -i HOME="$HOME" USER="$USER" TERM="$TERM" LANG=en_US.UTF-8 \ + DEVELOPER_DIR="$dev_dir" \ + PATH="${lib.makeBinPath [ pkgs.bun pkgs.nodejs_24 pkgs.cocoapods ]}:/usr/bin:/bin:/usr/sbin:/sbin" \ + bunx expo run:ios --no-bundler "$@" + ''; + }; <@endif> languages.go.enable = true; languages.typescript.enable = true; @@ -100,6 +117,12 @@ on = "always"; }; }; + # Reuses the Metro bundler started by expodev. + expoios = lib.mkIf pkgs.stdenv.isDarwin { + exec = "run-ios"; + cwd = "./apps/mobile"; + after = ["devenv:processes:expodev@started"]; + }; <@endif> sqlwatcher = { exec = "sqlc generate";