feat(cmd): support iOS development on Linux via xtool#5
Open
ezynda3 wants to merge 4 commits into
Open
Conversation
- add cmd/irgo/apple_linux.go: build the iOS xcframework on non-macOS
hosts using the xtool Darwin SDK, with generated xcrun/xcodebuild/lipo
shims that satisfy gomobile's Xcode expectations
- fix gomobile c-archive output with Apple libtool -static so ld64.lld
links the framework (Go archives lack a __.SYMDEF table of contents)
- route 'irgo build ios' / 'irgo run ios' to the xtool path on Linux:
build, sign, install, and launch on a USB/network device
- scaffold a SwiftPM/xtool project at ios/App (UIKit-only entry point to
avoid SwiftUICore dyld crashes on iOS < 18), mirrored to the framework
reference tree
- dev mode uses the machine's LAN IP for the device-reachable dev server
(override with IRGO_DEV_SERVER); allow the dev host in the WebView
navigation policy
- lock WebView zoom for a native feel: viewport meta + injected
WKUserScript, no link previews, tap-highlight/touch-callout CSS reset
- embed scaffold templates with 'all:' so dotfiles (ios/App/.gitignore)
are included; add {{PROJECT_IDENT}} substitution producing an Apple
provisioning-safe bundle ID fragment (alphanumerics only — the App
Store Connect API rejects underscores in App ID names)
- narrow the repo .gitignore 'irgo' pattern to '/irgo' so it only
ignores the root binary instead of hiding new files under cmd/irgo/
- update CLI help, install-tools hints, .gitignore, and README docs
Verified end-to-end on Linux: 'irgo new', 'go build ./...', and
'irgo run ios' building, signing, installing, and launching on a
physical iPhone over USB.
…ut.css Mobile builds embed static assets into the Go framework at build time (static/embed.go), so if static/css/output.css was never generated it is permanently missing from the installed app and every page renders unstyled. Found by deploying a freshly scaffolded project to a device without running the documented 'bun install' step first. - 'irgo new' now runs 'bun install && bun run css' (npm fallback) so scaffolded projects are styled and runnable out of the box; the "Next steps" hint is only printed when neither tool is available - ensureMobileBuildSetup() warns when static/css/input.css exists but output.css does not, covering 'irgo build ios|android' and 'irgo run ios|android' for existing projects
Rapidly re-triggering a Datastar action (e.g. mashing a button wired to @get) makes Datastar abort the previous in-flight fetch. WebKit then calls stop(_:) on the scheme handler for that task — but the handler ignored it and, once the Go bridge call finished, still invoked didReceive/didFinish on the dead task. WebKit raises NSInternalInconsistencyException ("task has already been stopped") for that, crashing the app. Track live tasks in a main-thread-confined set (WebKit delivers both start and stop on the main thread, and the completion is dispatched there too, so no locking is needed). stop(_:) removes the task; the completion silently drops the response if the task is no longer alive. Also removes the unused mimeType local that produced a compiler warning. Applied to all four copies of the handler: the SwiftPM app (ios/App), the framework reference (ios/Irgo), and both scaffold templates. Reproduced and verified on a physical iPhone: mashing "Trigger Burst" in the scaffolded demo crashed the app before, survives now.
…ge conflicts PR stukennedy#4 (android-end-to-end) introduces the same 'irgo new' improvements this branch needs: //go:embed all:templates, the {{PROJECT_IDENT}} substitution, sanitizeIdentifier, and the bun/npm + Tailwind CSS build step. Make every overlapping hunk byte-identical to PR stukennedy#4's version so git resolves them cleanly whichever PR merges first: - adopt PR stukennedy#4's sanitizeIdentifier verbatim (Java/Kotlin package semantics: non-alphanumerics become underscores) - adopt PR stukennedy#4's comment wording on the bun/tailwind install block - keep the {{PROJECT_IDENT}} content substitution line identical - adopt PR stukennedy#4's root .gitignore verbatim (anchored /irgo plus Android and mobile build outputs — all relevant here too) - move the scaffold .gitignore iOS block below the Dependencies section, away from the spot where PR stukennedy#4 inserts its Android block - replace runtime.GOOS checks in commands.go with an isDarwinHost() helper in apple_linux.go, so commands.go needs no new import at the line where PR stukennedy#4 adds "regexp" Apple's provisioning API rejects underscores in App ID names, so the iOS bundle ID can no longer ride on {{PROJECT_IDENT}}. Introduce a separate {{BUNDLE_IDENT}} placeholder backed by sanitizeBundleIdent (letters and digits only), which lives in apple_linux.go — a file PR stukennedy#4 does not touch. The substitution line is added after {{GO_VERSION}}, outside any region PR stukennedy#4 modifies. Tests updated to match: TestSanitizeIdentifier now asserts PR stukennedy#4 semantics; TestSanitizeBundleIdent covers the Apple-safe variant. Verified with a trial merge: main + PR stukennedy#4 + this branch now merges with zero conflicts in either order.
Author
|
Heads-up for reviewers: the latest commit (cc5fdcd) aligns this branch's overlapping scaffold edits with #4 so the two PRs merge cleanly in either order — verified with trial merges (zero conflicts both ways). Specifics:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds first-class iOS development on Linux.
irgo build iosandirgo run iosnow work on non-macOS hosts by cross-compiling with the xtool Darwin SDK and deploying to a physical USB/network-connected iOS device — no Mac required.How it works
cmd/irgo/apple_linux.go— buildsbuild/ios/Irgo.xcframeworkon Linux. gomobile assumes an Xcode toolchain (xcrun/xcodebuild/lipo), so small generated shim scripts answer the handful of invocations gomobile actually makes, backed by the xtool Darwin SDK (~/.swiftpm/swift-sdks/darwin.artifactbundle, override withIRGO_DARWIN_SDK) plus the Swift toolchain's clang. Only theios-arm64device slice is built (there is no Simulator on Linux).__.SYMDEFfix — Go's c-archive output has no archive table of contents, andld64.lldsilently skips archive members without one (producing "undefined symbol" errors at app link time). Applelibtool -staticfrom the SDK toolset rebuilds the framework archive with a proper TOC.ios/App— a new scaffold (Package.swift+xtool.yml+ UIKit-only entry point) consumes the xcframework as a binary target.xtool dev runbuilds, signs, installs, and the CLI then best-effort launches it viaxtool launch. UIKit-only on purpose: SwiftUI apps built against recent SDKs pull in SwiftUICore, which doesn't exist on devices running iOS < 18 and crashes at launch when cross-linked with ld64.lld. The existing Xcode project atios/Exampleis untouched and remains the macOS path.irgo run ios --dev) — a physical device can't reachlocalhost, so the dev server URL is derived from the machine's LAN IP (override withIRGO_DEV_SERVER=http://<host>:8080); the WebView navigation policy allows the configured dev host.WKUserScript), no long-press link previews, no pinch/double-tap zoom, tap-highlight/touch-callout CSS reset in the scaffold.macOS behavior is unchanged: the new code paths are only taken when
runtime.GOOS != "darwin".Other changes
//go:embed all:templatesso dotfiles (e.g.ios/App/.gitignore) reach generated projects.{{PROJECT_IDENT}}template substitution produces an Apple-provisioning-safe bundle ID fragment (alphanumerics only — the App Store Connect API rejects underscores in App ID names, found the hard way during device testing)..gitignorepatternirgonarrowed to/irgoso it only ignores the root binary instead of also hiding new files undercmd/irgo/.install-toolshints, scaffold.gitignore, and README updated.Testing
go test ./cmd/irgo/— new unit tests for the shim generation, SDK path mapping,xtool.ymlparsing, and the identifier sanitizer.irgo new <app>→ scaffold includesios/Appwith sanitized bundle IDgo build ./...in the scaffolded project ✅irgo build ios→build/ios/Irgo.xcframeworkbuilt and synced toios/App/Irgo.xcframework✅irgo run ios→ app built, signed, installed, and launched on a physical iPhone over USB ✅This is the same approach we shipped in starboard (the framework forked from irgo), ported back upstream.