Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Application openURL gets called a few seconds after didFinishLaunchingWithOptions

I have a deep linking feature in my app that works fine beside one case. I have a 3 different onboarding pages according to the url that opened the app. So when the app is launched i need to know what link(if any) opened the app and then present the right onboarding page. The problem is that i need to know what screen to present in the method:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

but i can only know if a deep link opened the app in

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation 

which gets called 5 seconds after did didFinishLaunchingWithOptions is called(i counted the seconds). So i have 5 seconds that i see a wrong onboarding page untill openURL is called(if it will be called) .

So my question is: is there any way to know if the app was launched from a url before or during didFinishLaunchingWithOptions?

By the way launchOptions in didFinishLaunchingWithOptions is nil when the app opens from a deep link

like image 250
aviv_elk Avatar asked Feb 20 '17 13:02

aviv_elk


2 Answers

The launch option key your are looking for is UIApplicationLaunchOptionsURLKey (Objective-C) / UIApplicationLaunchOptionsKey.url (Swift).
If you're targeting iOS 9 and upwards you only have to intercept a launch URL from

  • application:didFinishLaunchingWithOptions: (in case the app is not in memory yet)
  • application:openURL:options: (in case the app is already in the background).

Here's a minimalistic implementation of UIApplicationDelegate that should cover both cases - please note that a lot unrelated logic has been omitted for clarity:

Objective-C:

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSURL *url = launchOptions[UIApplicationLaunchOptionsURLKey];
    if (url) {
        // TODO: handle URL from here
    }

    return YES;
}

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {

    // TODO: handle URL from here

    return YES;
}

@end

Swift 5:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

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

        if let url = launchOptions?[.url] as? URL {
            // TODO: handle URL from here
        }

        return true
    }

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

        // TODO: handle URL from here

        return true
    }
}
like image 190
Olivier Avatar answered Nov 28 '22 02:11

Olivier


I just had a similar problem in iOS 13, but things have changed in iOS 13 because the UIWindowSceneDelegate has been introduced and may now do some of the work previously done by UIApplicationDelegate (depending on your app settings).

The answer by @Olivier in this thread was still very useful to me because it points out the two scenarios in which the URL scheme gets handled; namely when the app is not in memory yet, which calls for application:didFinishLaunchingWithOptions:, and when the app has already been loaded and is in the background, which second case calls for application:openURL:options:.

So, as I'm mentioning above, things are a little different since iOS 13 if you are using the default application template generated by XCode 11. I won't get into the details here, so here's an informative tutorial on the topic: Understanding the iOS 13 Scene Delegate.

But the key methods to modify if you are using the new approach with scenes are scene(_:willConnectTo:options:) (docs here) and scene(_:openURLContexts:) (docs here). The former is where to act upon the URL scheme when the app was not already loaded (so it kind of replaces application:didFinishLaunchingWithOptions:), and the latter is where to get the URL when the app was already in the background when the URL scheme was called (so this one replaces application:openURL:options:).

With scene(_:willConnectTo:options:), you can look for the URL of the URL scheme (if any) doing something like this:

if let url = connectionOptions.urlContexts.first?.url {
    // handle
}

For scene(_:openURLContexts:), you can look inside the URLContexts set.

I hope this helps!

like image 45
focorner Avatar answered Nov 28 '22 03:11

focorner