Search code examples
swifttoday-extensionios12ios-universal-links

iOS Today Widget Universal Links From Lock Screen and Notification Center


We have a today widget to deep link into the app. The deep links work just fine when the user accesses the widget from the home screen. However, when a user accesses the widget when the device is locked, or when the user slides down from the top of the screen, the links open in Safari.

I was wondering if anyone else has come across this issue, and if so, how they solved it.


Solution

  • Here was the solution we came upon (Swift 4.1). We needed to support a custom URL scheme to tell iOS that we can open links from the today widget. This uses a different UIApplication delegate function. Along with implementing func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool, we also need to implement func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool

    First, in Info.plist, we have our supported schemes under CFBUndleURLTypes.

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>todayWidgetScheme</string>
            </array>
        </dict>
    </array>
    

    Then, also in Info.plist, we also listed the scheme under LSApplicationQueriesSchemes.

    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>todayWidgetScheme</string>
    </array>
    

    Next, when opening the link from the today widget, set the url scheme to the iOS recognized todayWidgetScheme.

    func openAppFromTodayWidget() {
        if let url = URL(string: "https://url.com") {
            var components = URLComponents(url: url, resolvingAgainstBaseURL: true)
            components?.scheme = "todayWidgetScheme"
    
            if let todayWidgetUrl = components?.url {
                extensionContext?.open(todayWidgetUrl)
            }
        }
    }
    

    Finally, in AppDelegate.swift, when iOS asks the application to handle the universal link, set the original url scheme

    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        if url.scheme == "todayWidgetScheme" {
            var components = URLComponents(url: url, resolvingAgainstBaseURL: true)
            components?.scheme = "https"
    
            if let todayWidgetUrl = components?.url {
                // do your thing
                return true
            }
        }
    
        return false
    }