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

How to integrate with a pure SwiftUI app? #4

Open
hk05 opened this issue Aug 3, 2021 · 5 comments
Open

How to integrate with a pure SwiftUI app? #4

hk05 opened this issue Aug 3, 2021 · 5 comments

Comments

@hk05
Copy link

hk05 commented Aug 3, 2021

MacMenuBar looks great, so I wanted to try it on one of my apps. Since it is an existing app, I cannot use the templates. I have not been able to run MacMenuBar in in a "pure swiftUI" environment with an entry point like:

@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

var body: some Scene {
    WindowGroup {
        ContentView()
    }
    .commands {
        SidebarCommands() 
    }
}

}

Following instructions in this situation did not yield success.

Would it be possible to extend the (otherwise excellent) documentation with a setup instruction for configs like the above?

@chipjarred
Copy link
Owner

chipjarred commented Aug 7, 2021

Thank you for the compliments!

@main had recently been introduced when I implemented the framework that became MacMenuBar, and the app I was using it in used the older Cocoa app delegate model. I do plan to update it for use with pure SwiftUI, but I haven't gotten around to working through the details. In the mean time, I don't think it would be too difficult in your case since you're using an explicit app delegate, and actually helping you do it will help me make progress toward updating MacMenuBar for the general case, if you don't mind some answering questions and doing some experimentation.

First the obvious thing: Does your AppDelegate file import MacMenuBar and call setMenuBar(to:) in its applicationDidFinishLaunching method like the project template code does? Assuming that code is in place, I'm guessing that WindowGroup's .command may be overriding the menu bar that was set up in the AppDelegate, because I'm guessing it's called after applicationDidFinishLaunching. Print statements after setMenuBar(to:) and SideBarCommands() would verify if that's the case (or just set break points and note when they they are hit).

If the project is in a public repo, providing the link would allow me to answer a lot of questions myself from your specific project. Otherwise could you confirm my suspicion is correct?

@hk05
Copy link
Author

hk05 commented Aug 8, 2021

Thanks for your reply.
Adding some print statements proves that .commands { SomeCommands() } in SwiftUI's MyApp is being called before setMenuBar(to: MainMenuBar()) in the AppDelegate.

PS: Unfortunately I do not have my project in a public repo, but you could generate a new Xcode 'pure' SwiftUI project, add your project package and set it up with something like:

@main
struct MyApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .commands {
            SomeCommands()
        }
    }
}

struct SomeCommands: Commands {
    init() {
        print("TEST MENUBAR IN COMMANDS")
    }
    var body: some Commands {
        CommandGroup(after: .newItem) {
            Section {
                Button("Blah Folder", action: { print("bla") })
            }
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationWillFinishLaunching(_ notification: Notification) {
        setMenuBar(to: MainMenuBar())
        print("TEST MENUBAR IN applicationWillFinishLaunching")
    }
}

@mgriebling
Copy link

mgriebling commented Dec 17, 2022

Any progress here? I'm running into a similar issue in trying to integrate MacMenuBar with a pure SwiftUI App.
For example my @main code looks like this:

@main
struct TestStuffApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
}

@chipjarred
Copy link
Owner

chipjarred commented Dec 18, 2022

@mgriebling, @hk05

I have found a solution, but it feels like a hack, so I haven't decided yet if I want to incorporate it directly into MacMenuBar. The problem boils down to SwiftUI overwriting MacMenuBar's setting NSApp.mainMenu. I was doing that in the explicit app delegate model that SwiftUI originally used. SwiftUI's App implicitly provides its own app delegate, which sets the mainMenu itself, and even if you do provide your own app delegate, there's a kind of tug-of-war over who gets to set mainMenu.

The solution I found is to wait to set it until the main run loop starts processing async tasks. This works, but I kind of don't like it. I may end up incorporating it into the package anyway, simply because I haven't been able to think of a better solution yet.

In the mean time, you can easily do it in your own code:

import SwiftUI
import MacMenuBar

@main
struct TestStuffApp: App {
    init()
    {
        DispatchQueue.main.async {
            NSApplication.shared.delegate?.setMenuBar(to: MainMenuBar())
        }
    }
    
    var body: some Scene {
        WindowGroup {
            return ContentView()
        }
    }
}

Successfully setting mainMenu seems to be the only problem using SwiftUI's App. Please give it a try, and post here whether it works for you. I'll leave this issue open for now to get feedback.

@chipjarred
Copy link
Owner

chipjarred commented Dec 18, 2022

I decided to incorporate the change into SwiftUI in v0.0.12. I also added an extension on SwiftUI's App so you can set the main menu more succinctly:

import SwiftUI
import MacMenuBar

@main
struct TestStuffApp: App {
    init()  { setMenuBar(to: MainMenuBar())  }
    
    var body: some Scene {
        WindowGroup {
            return ContentView()
        }
    }
}

@mgriebling from my tests, this should fix the issue, but I still would like to get confirmation as to whether this solves problem for you before I close the issue.

@hk05 I also updated the README to reflect this set up.

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