Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep Links with AppDelegate and SceneDelegate

I am trying to implement deep links to navigate to posts on an app, it was an older project so I had to add the SceneDelegate class. The deep link implementation works only when the app is active or in background. If the app has not been loaded the deep link will not work. I've seen many posts and tutorials on this and have not found out why, has anyone had similar issues?

In the AppDelegate class I have added implementation to handle links for the following functions:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {}

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {}

func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {}

In SceneDelegate I implement handling the links in the following functions:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {}

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

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {}

the implementation in those functions looks like this:

let navigator = Navigator()
navigator.getDesination(for: url)


func getDesination(for url: URL){
    let destination = Destination(for: url)
    let ivc = InstantiateViewController()
    switch destination {
    case .post(let postID):
        ivc.openPostVC(id: postID, showComment: true, commentID: nil)
    case .user(let userID):
        ivc.openProfileVC(userID: userID)
    default:
        break
    }
}

enum Destination {
    case post(Int)
        
    case user(Int)
            
    case feed(String)
            
    case store
            
    case safari
            
    init(for url: URL){
        if(url.pathComponents[1] == "p"){
            self = .post(Int(url.pathComponents[2])!)
        } else if(url.pathComponents[1] == "user") {
            self = .user(Int(url.pathComponents[2])!)
        } else if(url.pathComponents[1] == "store") {
            self = .store
        } else if(url.pathComponents[1] == "s") {
            self = .feed(url.pathComponents[2])
        } else {
            self = .safari
        }
    }
}


func openProfileVC(userID: Int){
    let service = UserPool.shared.request(for: userID)
                    
    let storyboard = UIStoryboard(name: "Profile", bundle: nil)
    let profileVC = storyboard.instantiateViewController(withIdentifier: "ProfileView") as! ProfileViewController
    profileVC.userService = service
    profileVC.shouldNavigateToHome = true
    profileVC.shouldNavigateToHomeAction = {
        self.loadMainStoryboard()
    }
        
    let navigationVC = UINavigationController(rootViewController: profileVC)
    navigationVC.view.backgroundColor = .white
    
    if #available(iOS 13.0, *) {
        guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate else {return}
        sceneDelegate.window?.rootViewController = navigationVC
        sceneDelegate.window?.makeKeyAndVisible()
    } else {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
        appDelegate.window?.rootViewController = navigationVC
        appDelegate.window?.makeKeyAndVisible()
    }
}

The websites app-site-assocation file looks like this and have added associated domain in Xcode:

{"applinks":{"apps":[],"details":[{"appID":"{my ID}","paths":["*"]}]},"webcredentials":{"apps":["{my ID}"]}}
like image 236
ShedSports Avatar asked Mar 07 '26 11:03

ShedSports


2 Answers

In iOS 13 and later with a scene delegate your app can observe the incoming universal link event at launch like this:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let url = connectionOptions.userActivities.first?.webpageURL {
       // ... or might have to cycle thru multiple activities
    }
}

If the app was already running you use this:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let url = userActivity?.webpageURL {
        // ...
    }
}

(I have a very simple downloadable demo app, and it proves that this really does work. I do not understand the claim that it does not; perhaps the problem is a failure to understand how to test.)

like image 99
matt Avatar answered Mar 09 '26 01:03

matt


from apple docs:

If your app has opted into Scenes, and your app is not running, the system delivers the URL to the scene(:willConnectTo:options:) delegate method after launch, and to scene(:openURLContexts:) when your app opens a URL while running or suspended in memory.

Full example:

In Scene delegate when app is terminated:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let url = connectionOptions.userActivities.first?.webpageURL
}

and for when app is background or foreground:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    let url = URLContexts.first?.url
}
like image 36
Mahdi Moqadasi Avatar answered Mar 08 '26 23:03

Mahdi Moqadasi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!