3

I have a simple SwiftUI app with an onOpenURL handler to handle Universal Links, and my target is macOS:

var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
                .onOpenURL { url in
                    print("Inside onOpenURL....")
                 }
         }
    }

I have also configured the Associated Domains capability, and the apple-app-site-association file in my domain, so when opening the link to that domain, my app is opened instead of Safari. So, when debugging the app, and clicking a Universal Link (for example, in Notes app), to open my app (which it does), I expect to see the "Inside onOpenURL...." message in the output window in Xcode, meaning the onOpenURL handler is handling the universal link.

But this does not happen. Every time I click the universal link, the app is opened but nothing is displayed in Xcode. This is in macOS Big Sur 11.1, and also 11.2 with a recent update.

However, if the same code is used to target an iOS 14 app, this works correctly, without changing any code, just the target of the app.

Trying many things, I also discovered that if I used a custom URL scheme (myapp:// for example), the onOpenURL handler is called, in my macOS app. I can see the "Inside onOpenURL...." message. But this does not happen with universal links, and my app is targeted to macOS and universal links are required.

Does anyone found this issue, and how to solve it?

Thanks

dacasta
  • 49
  • 4
  • I am in exactly the same place now. Except it is even worse with Monterey beta. On Big Sur, I see that the app downloads the apple-app-site-association file, and opening a universal link does switch over to the macOS app, but does not receive the URL (exactly as in your case). On Monterey, I cannot get the same code to switch to the app as all, and it does not download the apple-app-site-association file. – Jaanus Aug 21 '21 at 11:00
  • Uh… I had forgot to enable developer mode on Monterey after I remembered to do that, I see that Monterey works the same as Big Sur - Universal Link switches to app, but onOpenURL handler is not called. – Jaanus Aug 21 '21 at 12:11
  • Thanks @Jaanus. I think this is a bug, since the documentation says the same behavior should be expected on all platforms for onOpenURL, and I have already submitted a Feedback to Apple, can you do the same please? I submitted since February and have not received any input from Apple. – dacasta Aug 24 '21 at 02:07
  • I agree with you. It seems like a bug in macOS SwiftUI app lifecycle. I have also confirmed that Universal Links work correctly as expected with AppKit lifecycle, where you receive the app delegate call `application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void)` for incoming universal links. I have filed feedback FB9544271, but I don’t expect much/quick response. – Jaanus Aug 25 '21 at 05:48
  • Thanks @Jaanus. I also tried using the AppDelegate adaptor, but found the same behavior. Have you tried this? – dacasta Aug 26 '21 at 16:38
  • Yes, I tried. If you are using the app delegate adaptor with SwiftUI lifecycle on macOS, then the above delegate method is simply not called for incoming universal links either. – Jaanus Aug 27 '21 at 13:47

2 Answers2

4

It looks like you have to use a different function for macOS sometimes.

.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
    guard let url = userActivity.webpageURL else { return }
    print("Inside onContinueUserActivity....")
}

I'd recommend keeping onOpenURL in case there are some circumstances where one works and the other doesn't (for example, onOpenURL will be triggered if you manually launch a deep link from within your own app).

Colin Tremblay
  • 357
  • 1
  • 9
1

Had the exact same problem and Colin's answer didn't work for me for some reason! I had to create an App Delegate, make it an ObservableObject, put a published property to observe later, and implement the "open" method:

class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
    @Published var urls = [URL]()

    func application(_ application: NSApplication, open urls: [URL]) {
        self.urls = urls
    }
}

Then of course add the adaptor to the @main file:

@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

Then in the ContentView() or wherever add it as an @EnvironmentObject:

@EnvironmentObject private var appDelegate: AppDelegate

...and only then could I use it, something like:

.onChange(of: appDelegate.urls) { urls in
    dump(urls)
}

No idea if it's a good solution or not, but it's the only thing that worked for me. Got the idea from here: https://developer.apple.com/documentation/swiftui/nsapplicationdelegateadaptor and then here: https://developer.apple.com/documentation/appkit/nsapplicationdelegate

Suges
  • 11
  • 2