17

I'm converting an old macOS app to SwiftUI, and I've run into a problem with the new SwiftUI WindowGroup.

The old app is a single window application (basically a glorified timer) and an URL scheme (appname://15) can be used to change the timer.

I've attempted to recreate the old URL Scheme functionality using the onOpenURL method, but whenever the URL Scheme triggers, the app opens a new window and I can't figure out how to stop that from happening.

var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL(perform: { url in
                    print("\(url)") // This is just debug code
                })
        }.commands {
            CommandGroup(replacing: .newItem, addition: { })
        }
    }

I don't mind if the new version of the app allows multiple timers, but the url scheme is definitely not intended to open up new windows every time it's used.

How do I stop onOpenURL from launching new windows? I'm converting the app specifically to learn SwiftUI, but if it's not possible to do this behavior in SwiftUI, I'm willing to mix and match in some AppKit code.

Simon
  • 485
  • 4
  • 9

1 Answers1

19

The article, "Open window / scene in SwiftUI 2.0 on macOS" shows how to open windows. I took the pieces of it and made it to where it would simply open my window instead of a different one.

var body: some Scene {
 WindowGroup {
   ContentView()
     .handlesExternalEvents(preferring: ["{path of URL?}"], allowing: ["*"]) // activate existing window if exists
     .onOpenURL(perform: { url in
         print("\(url)") // This is just debug code
      })
   }.commands {
      CommandGroup(replacing: .newItem, addition: { })
   }
   .handlesExternalEvents(matching: ["{same path of URL?}"]) // create new window if doesn't exist
}

For those wanting to (try to) better understand preferring and allowing parameters. Paraphrased from Apple documentation:

preferring parameter is a Set of strings that are checked to see if they are contained in the targetContentIdentifier of this view (ContentView in this case) to see if this view prefers to handle the external event (openURL in this case) versus another view.

allowing parameter is a Set of strings that are checked to see if they are contained in the targetContentIdentifier of this view, allowing the view to handle the event.

Empty Sets are never matched. "*" are always matched.

Reference: Apple Documentation on handlesExternalEvents(preferring:allowing:)

Mr Rogers
  • 6,091
  • 2
  • 32
  • 34
  • 2
    Thanks! I was able to get this working. I ended up doing a matching: Set(arrayLiteral: "*") for the WindowGroup handlesExternalEvents so that it would accept anything sent using the correct URL Scheme. – Simon Mar 17 '21 at 05:24
  • 1
    I added some information on the `preferring` and `allowing`. I think `preferring` allows some weighting if you have multiple views handling external events with the same level of permission (set via `allowing`). It's not completely clear to me why you need both instead of requiring different matching set strings, but I only played around with this a little bit to get acquainted. – Mykel May 13 '21 at 05:58
  • Saved my day, I was working with `widgetURL` and was opening a new window thought it was a `widgetURL` bug (but wasn't) but actually seems to be the same behaviour. Thanks a lot! – user1046037 Sep 05 '22 at 09:43