Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App starts in the background when using "swift bundler run" or VSCode LLDB debugger [Solved with hack] #38

Open
benjiwolff opened this issue Jan 13, 2024 · 5 comments

Comments

@benjiwolff
Copy link

Problem

When using "swift bundler run" or running the App using VSCode LLDB Debugger, the app starts in the background. This can be tedious when the app is restarted often to see changes. The same does not happen when apps are started using Finder or Xcode.

Solution (Hack)

Explicitly activate the app from within. Here is the SwiftUI code that does this.

import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .task { await Activate() }
        }
    }

    // When starting the application using swift-bundler or debugging with LLDB in VSCode,
    // the application launches in the background unless we do this
    private func Activate() async {
        while !NSRunningApplication.current.activate() {
            try? await Task.sleep(for: .milliseconds(100))
        }
    }
}

This is not beautiful because we add unnecessary code into the released app. However, I could not find a way to activate the app from the outside since NSWorkspace.shared.openApplication() seems to do nothing on my M2 MacBook and NSRunningApplication would not attach to the app if it was started using Process.run().

Feel free to add this to the swift-bundler templates.

@stackotter
Copy link
Owner

Thanks for bringing this to my attention! It hasn't bothered me for ages cause I'm using a tiling window manager (so the app is always fully visible when launched, and easy to focus with my mouse or keyboard), but I do recall it being annoying before I moved to a tiling window manager.

I've implemented a partial fix for this in commit 75b5795 (the current latest commit). It brings apps to the foreground after launching them. This doesn't fix the issue with CodeLLDB since that's not interacting with Swift Bundler.

In the case of CodeLLDB, your hack might be the best solution for now, unless it's easy to fix in CodeLLDB, in which case we could just fork CodeLLDB.

@stackotter
Copy link
Owner

For anyone stumbling across this issue, here's a more backwards compatible version of the hack (.task is only available for macOS 12.0+),

import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
+               .onAppear {
+                   Task {
+                       while !NSRunningApplication.current.activate() {
+                           try? await Task.sleep(nanoseconds: 100_000_000)
+                       }
+                   }
+               }
            }
        }
    }
}

@j-o-sh
Copy link

j-o-sh commented Feb 24, 2024

Just as an additional thought on the topic. I came here, thinking that this was an Issue with Swift Bundler, until I noticed the same thing in a raw SwiftUi app (via swift run)... The terminal would simply keep focus and even clicking the app or selecting it from the Bar would not focus the app.

...until (by accident) I ran my app from the terminal without tmux. 😳

It seems that tmux does something strange that retains the focus when starting any UI stuff via swift run. This is where I am on a complete loss now.

Any ideas what could be going on here?
Are you using tmux as well or just a tiling window manager?

Cheers ✌️

(btw. Thanks for Bundler. It helps a ton! 🙏)

@benjiwolff
Copy link
Author

For me the App starts in the background when started from the terminal. I do not use tmux. I am not sure what you mean by running a "raw SwiftUI app with swift run". As far as I know, that is not a proper macOS app so I do not expect it to behave properly.

@stackotter
Copy link
Owner

(btw. Thanks for Bundler. It helps a ton! 🙏)

Glad that you're finding it useful!

...until (by accident) I ran my app from the terminal without tmux. 😳

That's strange, does it work without tmux for you? I don't use tmux, just yabai, but this has always been an issue even before yabai. The issue is that if you run an executable file in terminal, the terminal keeps focus in some weird way. As you've noticed, this isn't specific to Swift Bundler, but you wouldn't have known before Swift Bundler cause you'd never have made a SwiftUI app without Xcode.

This could be fixed by addressed Swift Bundler to run open YourApp.app instead, but then of course you don't get any stdout/logs! Not ideal.

Even if you manually run an app from terminal with .build/bundler/YourApp.app/Contents/MacOS/YourApp, the terminal keeps focus. I'd love to know what Xcode does to run apps. I might have to play around with fork and execve and see if that works any better than Process.

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

No branches or pull requests

3 participants