Skip to content

Modernised UI: SwiftUI entry, SettingsView, LogManager/Console#136

Merged
johnno1962 merged 16 commits into
johnno1962:2.0_overhaulfrom
maatheusgois-dd:maatheusgois-dd/2.0_overhaul-ui
Apr 20, 2026
Merged

Modernised UI: SwiftUI entry, SettingsView, LogManager/Console#136
johnno1962 merged 16 commits into
johnno1962:2.0_overhaulfrom
maatheusgois-dd:maatheusgois-dd/2.0_overhaul-ui

Conversation

@maatheusgois-dd

@maatheusgois-dd maatheusgois-dd commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports the AppKit/XIB → SwiftUI overhaul from maatheusgois-dd/main onto 2.0_overhaul, split into 5 logical commits plus a small build-config commit. No behavioural changes in the backend (MonitorXcode, NextCompiler, FrontendServer, ControlServer) — the AppDelegate keeps its @IBAction signatures and gets CompatMenuItem / CompatButton / CompatTextField shims that route reads/writes to ConfigStore.shared, so legacy mutation call sites still compile.

Commits (in order):

  • chore(assets): migrate to AppIcon catalog + INJECTION_* imagesets; drop App.icns + legacy .tif files.
  • refactor(config): introduce ConfigStore (ObservableObject), drop Defaults.swift, keep Defaults as a typealiased back-compat shim.
  • feat(ui): SwiftUI @main (InjectionNextApp) + StatusMenuView; delete MainMenu.xib, main.m, and the AppDelegate @IBOutlet surface; add Compat* shims. Info.plist drops NSMainNibFile / NSPrincipalClass.
  • UI: SettingsView — 10 panels (General, Xcode, BuildSystem, Compiler, Injection, Devices, FileWatcher, Network, Tracing, Advanced) observing ConfigStore; new Window(\"settings\") scene. Adds AppDelegate.applyDeviceSettings(enabled:restartServer:) so DevicesSettingsView doesn't have to forge an NSMenuItem.
  • UI: LogManager + ConsoleView — central ObservableObject logger with ring buffer, dedupe, uncaught-exception / fatal-signal capture, stdout/stderr hijack mirrored to the real fds; Window(\"console\") scene; removes the ad-hoc LogBuffer class from ControlServer.swift in favour of typealias LogBuffer = LogManager. Call sites migrated from optional ?. to non-optional .shared.
  • build: persist CODE_SIGN_IDENTITY=- and empty DEVELOPMENT_TEAM for the macOS SDK so local xcodebuild no longer needs those flags.
  • docs: add TODO.md roadmap.

MACOSX_DEPLOYMENT_TARGET bumped from 10.13 → 13.0 (required for @Published / MenuBarExtra). DLKit SPM ref moved to main to pick up the public dlOpenMode override.

Roadmap (from TODO.md)

Done

  • DLKit: public static var dlOpenMode override — merged upstream, consumed via SPM.
  • Modernised UI / AppKit → SwiftUI migration — this PR.
  • Swift 6 / strict-concurrency audit — nothing in the ported UI requires Swift 6; language mode stays Swift 5.

Next

  • Bump version to 2.0.0 (MARKETING_VERSION, tag after merge).
  • Fix red internal-Xcode icon when launching Xcode from the app (non log-parsing path).
  • Re-sync submodules against latest heads (after upstream merges).
  • Settings / preferences refactor — continue consolidating ConfigStore; migrate remaining UserDefaults direct reads in the backend.
  • Logging + error surfacing improvements (surface last compile error in status menu; persist level filter; truncation indicator).
  • README / docs update for the 2.0 flow.
  • Smoke-test matrix: Xcode 15.x / 16.x / 26.x × macOS 14 / 15.

Test plan

  • xcodebuild -project App/InjectionNext.xcodeproj -scheme InjectionNext clean build on Xcode 26.3 → BUILD SUCCEEDED.
  • Launch the .app, smoke-test menu bar entries (Launch Xcode, Select Project…, Watch Project…, Unhide Symbols, Open Console…, Settings…, Quit).
  • Open Settings window, switch through all 10 tabs, toggle a value, relaunch and confirm it persists (ConfigStore round-trip).
  • Open Console window, verify print / NSLog / Swift runtime warnings appear; Clear works.
  • Status icon colour transitions blue → purple → orange → green/yellow/red during an injection cycle.
  • Project picker: 0 / 1 / 3 xcodeproj scenarios; Browse and Select Project actions work.

Made with Cursor

Replace legacy App.icns + InjectionBusy/Error/Idle/OK/Ready.tif with
a proper asset catalog: AppIcon.appiconset (10 sizes) and six status
imagesets (INJECTION_BLUE/GREEN/ORANGE/PURPLE/RED/YELLOW).

Info.plist: swap CFBundleIconFile ("App.icns") for CFBundleIconName
("AppIcon").

pbxproj: drop file refs + build-phase entries for the removed icons.

Visual output only; no runtime behaviour change. Status image lookups
by these names will be wired up in the SwiftUI StatusMenuView commit.

Made-with: Cursor
Replace the small Defaults struct with an ObservableObject ConfigStore
that is the single source of truth for all user-facing configuration.
It owns persistence to UserDefaults, publishes change notifications for
SwiftUI Views (landing in a later commit), and exposes discovery
helpers for available Xcodes and codesigning identities.

To keep call-site churn out of this commit, ConfigStore ships with a
backward-compat Defaults shim that forwards to ConfigStore.shared, so
AppDelegate / MonitorXcode / NextCompiler / ControlServer / etc. keep
compiling unchanged. Those will be cleaned up alongside the SwiftUI
entry point.

Project changes:
- Delete App/InjectionNext/Defaults.swift
- Add App/InjectionNext/ConfigStore.swift (Xcode, Build System,
  Compiler, Injection, Devices, Tracing, File Watcher, Network and
  Advanced sections + project discovery helpers + picker window used
  when multiple .xcodeproj/.xcworkspace are found)
- Bump InjectionNext app deployment target 10.13 -> 13.0 (required by
  @published / SwiftUI MenuBarExtra work that follows)
- Switch the DLKit SPM dependency from tagged 3.5.6 to branch 'main'
  so we resolve the commit that exposes the configurable dlOpenMode
  knob ConfigStore binds to (johnno1962/DLKit hasn't cut a tag yet).

No behavioural change yet; the UI is still XIB-based and reads the
same UserDefaults keys.

Made-with: Cursor
Migrate the app's launch path from main.m + MainMenu.xib to a SwiftUI
@main entry (InjectionNextApp) backed by a MenuBarExtra and an
NSApplicationDelegateAdaptor. The menu bar dropdown is now driven by
StatusMenuView, which observes ConfigStore for status, watchers, build
system, and client state.

AppDelegate is slimmed to a shim over ConfigStore: IBOutlets are gone
and their call sites keep compiling via lightweight Compat* types
(CompatMenuItem/Button/TextField) whose reads/writes route to
ConfigStore. main.m, MainMenu.xib, and the XIB-specific Info.plist
keys (NSMainNibFile, NSPrincipalClass) are removed.

Minimal non-UI adjustments to keep the link clean:
- ControlServer: pass a fresh NSMenuItem() to deviceEnable instead of
  the removed outlet, drop the selectXcodeItem.toolTip write.
- FrontendServer: drop optional-chaining on patchCompilerItem (shim
  returns a non-optional NSMenuItem).

Made-with: Cursor
Ports the modern SwiftUI settings UI from main into 2.0_overhaul:
- SettingsView as the shell with tabbed navigation
- General, Xcode, BuildSystem, Compiler, Injection, Devices,
  FileWatcher, Network, Tracing, Advanced settings panes
- Bridges device enable toggle to AppDelegate.applyDeviceSettings(enabled:)
- Adds a SwiftUI Window scene ("settings") to InjectionNextApp

All panels observe ConfigStore and mutate published properties so the
legacy non-UI backend continues to see state changes via the
CompatMenuItem/CompatButton/CompatTextField shims in AppDelegate.

Made-with: Cursor
- LogManager: centralized ObservableObject logger with timestamped
  entries, ring buffer, duplicate suppression, uncaught-exception /
  fatal-signal handlers, and a readLoop that hijacks STDOUT/STDERR
  while still mirroring bytes to the real fds.
- Removes the old ad-hoc LogBuffer class from ControlServer.swift;
  kept LogBuffer as a typealias to LogManager for back-compat.
- Updates InjectionServer / NextCompiler / ControlServer call sites
  to the non-optional shared logger.
- AppDelegate now starts log capture unconditionally; ControlServer
  start remains gated on Defaults.mcpServer.
- Adds ConsoleView (SwiftUI) with filter/level/auto-scroll and an
  NSTextView-backed selectable log pane.
- InjectionNextApp exposes a new "console" Window scene; the status
  menu's "Open Console…" entry opens it via openWindow.

Made-with: Cursor
Persist CODE_SIGN_IDENTITY=- and an empty DEVELOPMENT_TEAM for the
macOS SDK in the InjectionNext target so local builds no longer
require passing those flags on the xcodebuild command line. Also
absorbs an Xcode-initiated reordering of existing pbxproj sections
(no functional change).

Made-with: Cursor
@maatheusgois-dd maatheusgois-dd marked this pull request as ready for review April 17, 2026 23:17
@johnno1962

johnno1962 commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Looks good. Did you need to change the code signing though? What was the problem with Defaults.swift?

@johnno1962

Copy link
Copy Markdown
Owner

Seems there is a missing setting "Device Testing". Looking at on-device injection you should only link the injection.dylib with deviceLibraries when the user enables this (and they are using the copy_bundle.sh script) otherwise you get:

🔥 ⚠️ dlopen failed dlopen(/private/var/mobile/Containers/Data/Application/F0F4088A-9A3F-46D8-A369-E4CB0B9A75F5/tmp//eval_injection_ViewController 2_1.dylib, 0x0002): Library not loaded: @rpath/XCTest.framework/XCTest

Previously, enabling "Device Injection" also unconditionally linked
-framework XCTest -lXCTestSwiftSupport into every injection dylib, so
apps that don't link XCTest themselves would crash at dlopen with:

  Library not loaded: @rpath/XCTest.framework/XCTest

NextCompiler already checks `AppDelegate.ui.deviceTesting?.state == .on`
to decide whether to add the testing -F/-L flags, but the SwiftUI port
routed that shim to `devicesEnabled`, so it was always on when devices
were on.

Split into its own flag:

- ConfigStore.deviceTesting (Bool, persisted as "deviceTesting")
- Defaults.deviceTesting back-compat shim
- AppDelegate.deviceTesting shim now binds to ConfigStore.deviceTesting
- AppDelegate.deviceTestingToggled(enabled:) exposes the legacy
  testingEnable behaviour (copy Run Script build phase to clipboard
  when turning on) to SwiftUI
- DevicesSettingsView: new "Device Testing" section with the toggle,
  footer explaining the copy_bundle.sh requirement, and greyed-out
  Linker Libraries field when off

Made-with: Cursor
@maatheusgois-dd

Copy link
Copy Markdown
Contributor Author

Hey @johnno1962!

Did you need to change the code signing though?

No, not functionally, the app still signs the same way when built through Xcode. I pinned CODE_SIGN_IDENTITY[sdk=macosx*] = "-" + empty DEVELOPMENT_TEAM so xcodebuild from the CLI works without my personal team. Before that, a plain xcodebuild … build failed with No "Mac Development" signing certificate matching team ID "9V5A8WE85E". Happy to drop the commit if you'd rather each contributor pin their own team.

What was the problem with Defaults.swift?

Nothing wrong with it per se, it just isn't observable. Defaults is a struct of static computed UserDefaults accessors, so SwiftUI has no way to re-render when a value changes (no @published, no objectWillChange). The new ConfigStore is a singleton ObservableObject over the same keys, which is what every new SwiftUI view binds to via @ObservedObject/@published. Persistence semantics are identical (same UserDefaults keys, same defaults). I kept a Defaults shim in ConfigStore.swift that forwards to ConfigStore.shared, so legacy call sites (Defaults.xcodeDefault, Defaults.deviceLibraries, etc.) still compile untouched.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports the modernized macOS UI overhaul from AppKit/XIB to SwiftUI, introducing a SwiftUI app entry point, a multi-panel Settings window, and an in-app Console backed by a centralized logger/config store.

Changes:

  • Replace main.m + MainMenu.xib status menu with SwiftUI @main + MenuBarExtra (StatusMenuView) and add dedicated settings/console window scenes.
  • Introduce ConfigStore (ObservableObject) to centralize/persist settings and keep legacy call sites compiling via compatibility shims.
  • Add LogManager with a ring buffer + stdout/stderr capture and migrate log call sites to a non-optional shared logger.

Reviewed changes

Copilot reviewed 35 out of 57 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
TODO.md Adds a 2.0 roadmap and tracking notes for follow-up work.
App/InjectionNext/main.m Removes legacy Cocoa entry point in favor of SwiftUI @main.
App/InjectionNext/InjectionNextApp.swift Adds SwiftUI app entry with menu bar extra + settings/console windows.
App/InjectionNext/AppDelegate.swift Removes XIB outlets; adds ConfigStore-backed compatibility shims and lifecycle setup.
App/InjectionNext/ConfigStore.swift Adds centralized observable/persisted configuration + project discovery/picker UI.
App/InjectionNext/LogManager.swift Adds centralized logger with ring buffer, dedupe, crash capture, and stdout/stderr interception.
App/InjectionNext/Views/StatusMenuView.swift Implements SwiftUI replacement for the legacy status menu.
App/InjectionNext/Views/SettingsView.swift Adds Settings window shell (sidebar + section routing).
App/InjectionNext/Views/GeneralSettingsView.swift Adds “General” settings/status panel.
App/InjectionNext/Views/XcodeSettingsView.swift Adds Xcode selection + launch/restart toggles and runtime status display.
App/InjectionNext/Views/BuildSystemSettingsView.swift Adds build-system selection (auto/Xcode/Bazel/SPM) and tool-path configuration.
App/InjectionNext/Views/CompilerSettingsView.swift Adds compiler interception UI and frontend path/status display.
App/InjectionNext/Views/InjectionSettingsView.swift Adds project path selection, watcher management, and injection behavior toggles.
App/InjectionNext/Views/DevicesSettingsView.swift Adds device injection/testing toggles and codesigning identity selection UI.
App/InjectionNext/Views/FileWatcherSettingsView.swift Adds injectable-pattern and latency settings UI.
App/InjectionNext/Views/NetworkSettingsView.swift Adds host/port/protocol info UI and client connection status.
App/InjectionNext/Views/TracingSettingsView.swift Adds trace mode + filter/framework/UIKit tracing configuration UI.
App/InjectionNext/Views/AdvancedSettingsView.swift Adds advanced toggles, dlopen mode selection, diagnostics, and reset-to-defaults UI.
App/InjectionNext/Views/ConsoleView.swift Adds in-app console UI with filtering and selectable NSTextView-backed log pane.
App/InjectionNext/NextCompiler.swift Migrates logging call sites from optional LogBuffer.shared? to non-optional .shared.
App/InjectionNext/InjectionServer.swift Routes alert/error/log messages into LogManager (via LogBuffer typealias).
App/InjectionNext/ControlServer.swift Removes ad-hoc LogBuffer class; uses LogManager for log APIs and updates device toggle behavior.
App/InjectionNext/FrontendServer.swift Adjusts patch/unpatch UI title update to the new (shimmed) menu item surface.
App/InjectionNext/Info.plist Switches to asset-catalog icon name and removes NSMainNibFile/NSPrincipalClass.
App/InjectionNext/Defaults.swift Removes old Defaults implementation (superseded by ConfigStore + back-compat shim).
App/InjectionNext/Base.lproj/MainMenu.xib Removes legacy XIB-based menu UI.
App/InjectionNext/App.icns Removes legacy .icns app icon in favor of an asset catalog.
App/InjectionNext/InjectionIdle.tif Removes legacy TIFF status icon asset (migrated to asset catalog).
App/InjectionNext/InjectionOK.tif Removes legacy TIFF status icon asset (migrated to asset catalog).
App/InjectionNext/InjectionBusy.tif Removes legacy TIFF status icon asset (migrated to asset catalog).
App/InjectionNext/InjectionReady.tif Removes legacy TIFF status icon asset (migrated to asset catalog).
App/InjectionNext/InjectionError.tif Removes legacy TIFF status icon asset (migrated to asset catalog).
App/InjectionNext/Assets.xcassets/Contents.json Adds new asset catalog container metadata.
App/InjectionNext/Assets.xcassets/INJECTION_BLUE.imageset/Contents.json Adds status icon imageset metadata (blue).
App/InjectionNext/Assets.xcassets/INJECTION_BLUE.imageset/INJECTION_BLUE.png Adds status icon image (blue).
App/InjectionNext/Assets.xcassets/INJECTION_GREEN.imageset/Contents.json Adds status icon imageset metadata (green).
App/InjectionNext/Assets.xcassets/INJECTION_GREEN.imageset/INJECTION_GREEN.png Adds status icon image (green).
App/InjectionNext/Assets.xcassets/INJECTION_ORANGE.imageset/Contents.json Adds status icon imageset metadata (orange).
App/InjectionNext/Assets.xcassets/INJECTION_ORANGE.imageset/INJECTION_ORANGE.png Adds status icon image (orange).
App/InjectionNext/Assets.xcassets/INJECTION_PURPLE.imageset/Contents.json Adds status icon imageset metadata (purple).
App/InjectionNext/Assets.xcassets/INJECTION_PURPLE.imageset/INJECTION_PURPLE.png Adds status icon image (purple).
App/InjectionNext/Assets.xcassets/INJECTION_RED.imageset/Contents.json Adds status icon imageset metadata (red).
App/InjectionNext/Assets.xcassets/INJECTION_RED.imageset/INJECTION_RED.png Adds status icon image (red).
App/InjectionNext/Assets.xcassets/INJECTION_YELLOW.imageset/Contents.json Adds status icon imageset metadata (yellow).
App/InjectionNext/Assets.xcassets/INJECTION_YELLOW.imageset/INJECTION_YELLOW.png Adds status icon image (yellow).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/Contents.json Migrates app icon to asset catalog app icon set metadata.
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_16x16.png Adds app icon raster (16x16).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png Adds app icon raster (32x32 for 16@2x).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_32x32.png Adds app icon raster (32x32).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png Adds app icon raster (64x64 for 32@2x).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_128x128.png Adds app icon raster (128x128).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png Adds app icon raster (256x256 for 128@2x).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_256x256.png Adds app icon raster (256x256).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png Adds app icon raster (512x512 for 256@2x).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_512x512.png Adds app icon raster (512x512).
App/InjectionNext/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png Adds app icon raster (1024x1024 for 512@2x).
App/InjectionNext.xcodeproj/project.pbxproj Updates build settings, deployment target, resources, SwiftUI sources, and DLKit dependency pin.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread App/InjectionNext/ConfigStore.swift
Comment thread App/InjectionNext/ConfigStore.swift
Comment thread App/InjectionNext/Views/InjectionSettingsView.swift
Comment thread App/InjectionNext/Views/ConsoleView.swift
Comment thread App/InjectionNext/AppDelegate.swift
Comment thread App/InjectionNext/AppDelegate.swift
@johnno1962

johnno1962 commented Apr 18, 2026

Copy link
Copy Markdown
Owner

We'll have to think about the code signing issue. At present, I build releases using "Product/Archive" which also builds all the bundles. That's something small to sort out. The deviceTesting fix works now thanks. Before, you needed to "Enable Devices" each time you launch the app by design and it is now persistent - that may be ok I guess. The bad news though is I had a hang once which I've not seen again here inside this closure somewhere in Combine:

    func setInjectionState(_ state: InjectionState) {
        DispatchQueue.main.async { self.injectionState = state }
    }

If you want me to I'll merge at this point let me know though I figure it can wait now the submodules are up to date. I'm amazed at how much you've changed and the app still works as far as I can make out.

@johnno1962

Copy link
Copy Markdown
Owner

Had to merge a couple of changes from main due to some of the code being shared with InjectionIII which has created a pair of conflicts which I hadn't anticipated.Apologies, they were very small changes which should be easy to rebase.

Conflicts:
- project.pbxproj: take upstream's DLKit 3.5.7 pin instead of our
  `branch = main` — 3.5.7 is where `dlOpenMode` landed, so the floating
  branch reference is no longer needed.
- NextCompiler.swift: keep upstream's move of LogBuffer.append into
  the #else branch (so INJECTION_III_APP doesn't log), combined with
  the non-optional `.shared` call this branch already migrated to.

Made-with: Cursor
@johnno1962

johnno1962 commented Apr 18, 2026

Copy link
Copy Markdown
Owner

Sorry I've had to do it again as the one line edit to NextCompiler.swift was wrong, this time including Info.plist. Let me know if you want me to attempt to resolve this. The version of the change I force pushed to main should have been:

     msg = "⏳ "+msg
     #else
     msg = prefix+msg
+    LogBuffer.shared?.append(msg, level: "info")
     #endif
     print(msg)
-    LogBuffer.shared?.append(msg, level: "info")
     for client in InjectionServer.currentClients {
         client?.sendCommand(.log, with: msg)
     }

@johnno1962 johnno1962 force-pushed the 2.0_overhaul branch 5 times, most recently from d0bc379 to 162fa3c Compare April 18, 2026 17:47
@johnno1962 johnno1962 changed the base branch from 2.0_overhaul to 2.0_overhaul_v2 April 18, 2026 18:26
@johnno1962 johnno1962 changed the base branch from 2.0_overhaul_v2 to 2.0_overhaul April 18, 2026 18:27
@johnno1962

Copy link
Copy Markdown
Owner

PR is not quite correct in NextCompiler.swift, it's obvious how. I think I'll quit while we're ahead and can merge (phew)

Merge-resolve aftermath: two LogBuffer.append calls were coexisting
inside the #else branch — the original pre-prefix one from an earlier
resolution and John's corrected post-prefix one from johnno1962#138. Keep only
the post-prefix call so log entries carry the APP_PREFIX, and keep
the non-optional .shared migration from this branch.

Made-with: Cursor
@maatheusgois-dd

maatheusgois-dd commented Apr 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @johnno1962 so much for bearing with the merge ping-pong, and for the patience on both passes. Totally happy to keep iterating, happy to rework NextCompiler.swift in a follow-up, whichever is less painful for you.

Also want to flag: the setInjectionState Combine hang you hit is almost certainly the unconditional DispatchQueue.main.async closure re-entering when the caller is already on main. Smallest safe fix is a main-thread fast path:

func setInjectionState(_ state: InjectionState) {
    if Thread.isMainThread {
        injectionState = state
    } else {
        DispatchQueue.main.async { [weak self] in self?.injectionState = state }
    }
}

@johnno1962

Copy link
Copy Markdown
Owner

Looking good now, thanks for the fix. I've been doing some testing and the red Xcode was related to running injectionNext.app from the debugger, killing Xcode than launching it from inside the app. Everything is fine if you use the command line if you re-enable code signing. I was wondering if the Settings menu item shouldn't be closer to the top, before the "Launch Xcode" its even and if you want to make your InjectionState fix though I'm not convinced that can be the answer if it was an asynchronous call to main.

If you want to make those changes I'd like to merge your PR to de-risk this branch a little and then I can look at what I need for code signing releases for which I use the Xcode distribution workflow and see if we can reach an agreement. My idea at the moment is to create a separate Archive configuration perhaps though I think in the end you'll need to update the team. It's a project not target setting so it's not so inconvenient to change. I wouldn't be trying to automate releases with a GitHub workflow at this stage. I'd certainly like to avoid a situation where there are competing releases.
I can increase the release cadence if you message me for a new release candidate.

Then I think we'd be ready to roll a release candidate and start to get feedback of the new version. What do you mean by "happy to rework NextCompiler.swift in a follow-up"? It's not a model piece of code but if we start changing the functional side of things that involves a re-test and there are many modalities to check. I'd rather focus on the UI renovation and Bazel changes for now. How do you want me to merge? merge/squash/rebase? This branch itself will get squashed onto main as I like to keep that uncluttered.

@johnno1962 johnno1962 merged commit 5bdc8a8 into johnno1962:2.0_overhaul Apr 20, 2026
@johnno1962

Copy link
Copy Markdown
Owner

I've squashed this PR for now so I can build a release candidate. You should be able to add further commits and raise a new continuation PR to this same release branch.

@maatheusgois-dd

Copy link
Copy Markdown
Contributor Author

Thanks @johnno1962. Pushing two small follow-ups now:

  1. Status menu reorder, moving Settings up, before Launch Xcode.

  2. setInjectionState / updateWatchingDirectories main-thread fast path. You're right that it's not obviously the answer for an async-to-main hang — my hunch is that when the Combine publisher re-fires on the same tick, DispatchQueue.main.async back-to-back enqueues can deadlock against a view-update in progress (especially under MenuBarExtra). The fast path skips the hop when already on main, which at minimum removes one class of re-entrance. If the hang resurfaces I'll go deeper (probably wrap the @published in @mainactor or coalesce with .receive(on: DispatchQueue.main, latest:)).

On NextCompiler.swift: ignore my earlier "rework" line, I only meant "I'm open to more iteration if you wanted it." Nothing functional is wrong; happy to leave it as-is on this PR. If anything backend creeps in later I'll do it as a focused follow-up PR so you can scope the re-test.

On code signing: +1 to the separate Archive configuration route, keeps your distribution workflow intact. For day-to-day command-line builds I'll switch to updating the project team to ours (the 9V5A8WE85E default caught me off guard). Happy to revisit once you've sketched the Archive config and the release cadence you prefer.

On GitHub release automation: small clarification, I actually did wire it up on my main fork have a look if you're curious, so the mechanics work. The reason I'm not pushing for it here is a UX one: without the Apple Developer ID + notarization secrets in the GitHub Actions environment, the .app published by the workflow isn't signed by a trusted identity. macOS Gatekeeper then requires users to right-click → Open and manually grant permission on every launch, which is a bad first impression. Agreed it's not the right moment to fight with that, keeping releases under your archive workflow until we sort signing (and maybe notarization creds) is the sane path.

Merge preference: squash, the branch will end up squashed onto main anyway, no point preserving the merge-resolution ping-pong in the history. Your call though; merge is also fine.

@johnno1962

Copy link
Copy Markdown
Owner

Hi, I squashed while you were writing that and prepared a release and made a commit reverting you code signing changes for now. Not sure how automated release workflow will work without the team set which is required to build the iOS bundles. You could xcodebuild DEVELOPMENT_TEAM=YOURTEAM in your workflow I guess though build_bundles.sh might need a small change to pass it through.

On the hang it's a shame I didn't gather more information at the time but the change was definitely where I said. The code you suggested seems a sensible precaution however which may make it "go away". If you want to add a few commits to 2.0_overhual the release is a draft at the moment and I can incorporate them. The threading fix could be one thing I'd like to see included in the RC and shifting the Settings menu item up to be more prominent - No hurry.

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.

3 participants