Skip to content

feat(catalyst): run and build Mac Catalyst apps with ns run catalyst - #6

Merged
farfromrefug merged 2 commits into
mainfrom
feat/run-macos
Aug 11, 2026
Merged

feat(catalyst): run and build Mac Catalyst apps with ns run catalyst#6
farfromrefug merged 2 commits into
mainfrom
feat/run-macos

Conversation

@farfromrefug

@farfromrefug farfromrefug commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

PR Checklist

What is the current behavior?

There is no way to run a NativeScript app on macOS. #5 added a --catalyst flag to ns build ios, which produces a Mac Catalyst binary but has no run, deploy or LiveSync story.

What is the new behavior?

catalyst becomes a supported platform: ns build catalyst and ns run catalyst, the latter with full LiveSync.

On the name. This is deliberately not called macos. A separate upstream effort adds ns run macos for a native macOS app built against a macOS runtime — a different product. Mac Catalyst is the iOS app rebuilt against the macOS SDK, so it takes the name that says what it is.

A Catalyst app keeps every iOS convention — App_Resources/iOS, the iOS runtime package, each plugin's platforms/ios folder, the iOS Podfile, the iOS bundle — and diverges only in the directory it prepares into (platforms/catalyst) and the SDK it builds against.

  • Platform resolution. iOSProjectService reports iOS as the platform name so every iOS convention falls out for free, and projectRoot becomes the single place Catalyst differs. Two call sites rebuilt the platform directory from the platform name instead of reading projectRoot; both now use projectRoot, which already resolves hostProjectPath identically. Left as-is, a Catalyst build would have probed and deleted platforms/ios.
  • The Mac as a device. Build, deploy and LiveSync drive it through the existing pipeline. Everything is local: the .app is a directory on this machine, so the file system is a plain copy, install only records the built bundle, launch is open -n, and the log stream is log stream narrowed to the app and the runtime.
  • Watch-mode platform matching. Prepare events are now stamped with the platform the caller asked for rather than the platform data's name. run-controller pairs an event with a device by comparing the two, so for Catalyst — iOS platform data, Catalyst device — a file change recompiled but reached no device. The non-watch path already reported the requested platform; watchers and bundler processes are keyed by it too, since stopWatchers and stopBundlerCompiler are called with it. Nothing changes for ios/android/visionos, where both strings are identical.
  • Removes --catalyst, which this supersedes: the Catalyst build path is now selected by the platform rather than a flag on build ios.

Relationship to the upstream PR

The same work is proposed upstream as NativeScript#6125, and the Catalyst code here is byte-identical to it — verified line by line, including the four new files. That is deliberate: when NativeScript#6125 lands and this fork merges upstream/main, git sees the same additions on both sides and resolves them automatically.

A trial merge of the two confirms it. The only conflict my work adds is in getInstalledRuntimePackage, where the upstream patch inserts the Catalyst→iOS mapping directly above a block this fork already carries; resolution is to keep both sides. (That block — let packageName: string[] — is dead code: the .find() callback below declares its own local packageName, so nothing reads it. Worth removing separately.) The other conflicts in that trial — lib/config.ts and package-lock.json — appear identically when merging upstream/main into fork main with none of this work applied.

Testing

Verified end to end with OSS Weather on Apple Silicon, on this branch after the rename: ns build catalyst runs pod install, generates metadata for arm64-apple-ios<version>-macabi and links and signs the .app; ns run catalyst launches it, streams its logs, and syncs a file change into the running app — an added console.log came back through the CLI after the automatic restart.

No unit tests are included: the change is platform plumbing plus a device implementation that shells out to open, pkill and log, none of which the existing suite has a harness for.

vitest run reports 189 failures out of 1648 on this branch — and the identical 189 on main. Verified by running the suite on both and diffing the failing test names: zero new, zero fixed. The suite is already red on main; this PR does not change it.

Note for reviewers

Two runtime-side fixes are needed before a clean checkout can build a working Catalyst app, both unmerged:

  • the metadata generator needs the iOSSupport framework search paths, or the app launches into ReferenceError: UIDevice is not defined;
  • build_npm_ios.sh needs cp -R so the Catalyst xcframeworks keep their versioned-bundle symlinks, or codesign rejects the app.

iOS never hits either, because it uses shallow framework bundles and its own SDK paths.

farfromrefug and others added 2 commits August 11, 2026 15:20
Adds macOS as a supported platform. A Mac Catalyst app is the iOS app rebuilt
against the macOS SDK, so it keeps every iOS convention -- App_Resources/iOS,
the iOS runtime package, each plugin's platforms/ios folder, the iOS Podfile,
the iOS bundle -- and diverges only in the directory it prepares into,
platforms/macos, and the SDK it builds against.

That is the whole design: iOSProjectService reports iOS as the platform name so
every iOS convention falls out for free, and projectRoot is the single place
macOS differs. Two call sites rebuilt the platform directory from the platform
name rather than reading projectRoot, which would have made a macOS build probe
and delete platforms/ios; both now use projectRoot, which already resolves
hostProjectPath the same way.

The Mac is modelled as a device so build, deploy and LiveSync drive it through
the existing pipeline. Everything it does is local: the .app is a directory on
this machine, so the file system is a plain copy, install only records the built
bundle, launch is `open -n`, and the log stream is `log stream` narrowed to the
app and the runtime.

Watch-mode prepare events are stamped with the platform the caller asked for
rather than the platform data's name. run-controller pairs an event with a
device by comparing the two, so for Catalyst -- iOS platform data, macOS device
-- a file change recompiled but reached no device. The non-watch path already
reported the requested platform; watchers and bundler processes are now keyed by
it too, since stopWatchers and stopBundlerCompiler are called with it. Nothing
changes for ios/android/visionos, where both strings are identical.

Replaces the `--catalyst` build flag, which this supersedes.

Verified on a real app: `ns run macos` runs pod install, generates metadata for
arm64-apple-ios<version>-macabi, links and signs the .app, launches it, streams
its logs, and syncs a file change into the running app.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ns run macos` is being taken by a separate effort that builds a native macOS
app against a macOS runtime. That is a different product from a Mac Catalyst
build -- the iOS app rebuilt against the macOS SDK -- so this one takes the name
that says what it actually is: `ns build catalyst`, `ns run catalyst`,
preparing into platforms/catalyst.

Only the platform identifier changes; the build, device and LiveSync behaviour
is untouched. The renamed code is byte-identical to the upstream PR so the two
merge without conflict.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@farfromrefug farfromrefug changed the title feat(macos): run and build Mac Catalyst apps with ns run macos feat(catalyst): run and build Mac Catalyst apps with ns run catalyst Aug 11, 2026
@farfromrefug
farfromrefug marked this pull request as ready for review August 11, 2026 15:58
@farfromrefug
farfromrefug merged commit 382a655 into main Aug 11, 2026
7 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant