Detect app launch from WidgetKit widget extension

SwiftUI 2 life cycle

  1. Add widgetURL to your Widget view:
struct SimpleWidgetEntryView: View {
    var entry: SimpleProvider.Entry

    private static let deeplinkURL = URL(string: "widget-deeplink://")!

    var body: some View {
        Text("Widget")
            .widgetURL(Self.deeplinkURL)
    }
}
  1. Detect if the app is opened with a deeplink in onOpenURL:
@main
struct WidgetTestApp: App {
    @State var linkActive = false

    var body: some Scene {
        WindowGroup {
            NavigationView {
                VStack {
                    NavigationLink("", destination: Text("Opened from Widget"), isActive: $linkActive).hidden()
                    Text("Opened from App")
                }
            }
            .onOpenURL { url in
                guard url.scheme == "widget-deeplink" else { return }
                linkActive = true
            }
        }
    }
}

Here is a GitHub repository with different Widget examples including the DeepLink Widget.


If you are setting widgetURL or Link control for Widget UI then containing app opens with application(_:open:options:). You can set additional data in URL to know source.

If you are not using widgetUrl or link control then containing app opens with application(_:continue:restorationHandler:) and userInfo has WidgetCenter.UserInfoKey. That should tell you App opened from widget and information about user's interaction.


To detect an app launch from a WidgetKit widget extension where the parent application supports scenes you'll need to implement scene(_:openURLContexts:), for launching from a background state, and scene(_:willConnectTo:options:), for launching from a cold state, in your parent application's SceneDelegate. Also, add widgetURL(_:) to your widget's view.

Widget's View:

struct WidgetEntryView: View {
    
    var entry: SimpleEntry
    
    private static let deeplinkURL: URL = URL(string: "widget-deeplink://")!

    var body: some View {
        Text(entry.date, style: .time)
            .widgetURL(WidgetEntryView.deeplinkURL)
    }
    
}

Parent application's SceneDelegate:

// App launched
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let _: UIWindowScene = scene as? UIWindowScene else { return }
    maybeOpenedFromWidget(urlContexts: connectionOptions.urlContexts)
}

// App opened from background
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    maybeOpenedFromWidget(urlContexts: URLContexts)
}

private func maybeOpenedFromWidget(urlContexts: Set<UIOpenURLContext>) {
    guard let _: UIOpenURLContext = urlContexts.first(where: { $0.url.scheme == "widget-deeplink" }) else { return }
    print("🚀 Launched from widget")
}