20

I have a simple widget (medium-sized) with two texts, and what I want is to be able to perform a deep link to lead the user to a specific section of my app, but I can't seem to find a way to do so.

The view I have written (which is very simple):

HStack {
    Text("FIRST ITEM")    
    Spacer()
    Text("SECOND ITEM")
}

I have already tried to replace

Text("SECOND ITEM")

with

Link("SECOND ITEM destination: URL(string: myDeeplinkUrl)!)

but it doesn't work either.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
Another Dude
  • 1,204
  • 4
  • 15
  • 33

3 Answers3

47
  1. In the Widget view you need to create a Link and set its destination url:
struct SimpleWidgetEntryView: View {
    var entry: SimpleProvider.Entry

    var body: some View {
        Link(destination: URL(string: "widget://link1")!) {
            Text("Link 1")
        }
    }
}

Note that Link works in medium and large Widgets only. If you use a small Widget you need to use:

.widgetURL(URL(string: "widget://link0")!)
  1. In your App view receive the url using onOpenURL:
@main
struct WidgetTestApp: App {
    var body: some Scene {
        WindowGroup {
            Text("Test")
                .onOpenURL { url in
                    print("Received deep link: \(url)")
                }
        }
    }
}

It is also possible to receive deep links in the SceneDelegate by overriding:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)

You can find more explanation on how to use this function in this thread:


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

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • How is the link for .widgetUrl specified? How to define this url for a particular location in an app? Correct me if I am wrong but I think the first part of the url shows the URL scheme specified in the info section of the app but I am not able to figure out what exactly the second part of the url is? – Krits Dec 09 '20 at 12:07
  • @Krits Every url from the widget will be opened in the app. So the `scheme` can be a way to differentiate widget urls from other urls (e.g. from user notifications). The other path components can specify an action/location - it's up to you. You just need to use `onOpenURL(url:)` in a specific view and check if a particular URL arrived. – pawello2222 Dec 09 '20 at 13:56
  • So as mentioned in the above question, "widget://link0". What exactly link0 and link1 are? And how do we specify the action and or location in this url? – Krits Dec 09 '20 at 19:24
  • @Krits It's just a custom text describing the action. In `onOpenURL(url:)` you just check which url arrived (if it's link0 or link1...) and perform some action (show an alert, activate a navigation link...). – pawello2222 Dec 09 '20 at 22:02
  • Hi, can you please specify where exactly should I put onOpenUrl(url: ) in my app because I have created my app using Storyboard. – Krits Dec 10 '20 at 07:44
  • 1
    beautiful and complete answer – Mona Nov 14 '21 at 04:55
12

Also, you can do it using AppDelegate (if you not using SceneDelegate):

.widgetURL(URL(string: "urlsceheme://foobarmessage"))

// OR

Link(destination: URL(string: "urlsceheme://foobarmessage")!) {
    Text("Foo")
}

Set this code within AppDelegate

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
   let message = url.host?.removingPercentEncoding // foobarmessage
   return true
}
Andrew
  • 1,456
  • 15
  • 23
3

See docs on: Respond to User Interactions

When users interact with your widget, the system launches your app to handle the request. When the system activates your app navigates to the details that correspond to the widget’s content. Your widget can specify a URL to inform the app what content to display. To configure custom URLs in your widget:

  • For all widgets, add the widgetURL(_:) view modifier to a view in your widget’s view hierarchy. If the widget’s view hierarchy includes more than one widgetURL modifier, the behavior is undefined.
  • For widgets that use WidgetFamily.systemMedium or WidgetFamily.systemLarge, add one or more Link controls to your widget’s view hierarchy. You can use both widgetURL and Link controls. If the interaction targets a Link control, the system uses the URL in that control. For interactions anywhere else in the widget, the system uses the URL specified in the widgetURL view modifier.

For example, a widget that displays details of a single character in a game can use widgetURL to open the app to that character’s detail.

@ViewBuilder
var body: some View {
    ZStack {
        AvatarView(entry.character)
            .widgetURL(entry.character.url)
            .foregroundColor(.white)
    }
    .background(Color.gameBackground)
}

If the widget displays a list of characters, each item in the list can be in a Link control. Each Link control specifies the URL for the specific character it displays.

When the widget receives an interaction, the system activates the containing app and passes the URL to onOpenURL(perform:), application(_:open:options:), or application(_:open:), depending on the life cycle your app uses.

If the widget doesn’t use widgetURL or Link controls, the system activates the containing app and passes an NSUserActivity to onContinueUserActivity(_:perform:), application(_:continue:restorationHandler:), or application(_:continue:restorationHandler:). The user activity’s userInfo dictionary contains details about the widget the user interacted with. Use the keys in WidgetCenter.UserInfoKey to access these values from Swift code. To access the userInfo values from Objective-C, use the keys WGWidgetUserInfoKeyKind and WGWidgetUserInfoKeyFamily instead.

mfaani
  • 33,269
  • 19
  • 164
  • 293