loading...

iOS 13: launchOptions always nil? This is the reason & solution

nemecek_f profile image Filip Němeček ・2 min read

So today I spent quite some time puzzling over why I was always getting nil launchOptions argument in func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) and then finally found a solution.

The reason

The culprit is the new project structure for iOS 13 and above. In the past we were used as AppDelegate as the only class to handle app lifecycle events and responding to shortcuts, URL schemes and what not.

But with iOS 13 and more specifically iPadOS came the push to better support multiple screens for apps we got the SceneDelegate where most of the events now moved.

So even though the didFinishLaunchingWithOptions still looks the same:

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

You don't get any launchOptions here. It would be better if Apple simply changed the signature but for some reason (lets hope there at least is a reason) the argument is still present but always nil.

The solution

The solution to correctly getting launchOptions in iOS 13 and above is
to use SceneDelegate and the willConnectTo session which is automatically there with some basic code.
The full signature looks like this:

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

And the connectionOptions is new "reincarnation" of the trusty old launchOptions. To be fair to apple, this is much better to work with.

Say you want to respond to launch from shortcut item (the quick action available with long press on app icon). Previously you would have to reach into the options dictionary and do a cast.

Now it looks like this:

if let shortcut = connectionOptions.shortcutItem {
    // handle shortcut here
}

What about reacting to URL scheme? We have urlContexts set available and can simply pull the first item for basic implementation:

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

And that is it!

Hope this will save you some time when implementing this and you won't have to go full Sherlock 🕵 finding what is wrong...

One last thing

Since I have shown you how to react to shortcuts and URLs, let's see how to do these tasks when app is already running in the background.

We have this method to handle shortcuts:

 func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
}

And this one to handle URL launch:

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

Once again, this is much better than prior to iOS 13 changes. 🙂

Thanks for reading!

Posted on Apr 18 by:

nemecek_f profile

Filip Němeček

@nemecek_f

Primarily iOS developer, I also like Django and Python. And dabble with JavaScript occasionally. Love reading and coffee.

Discussion

markdown guide
 

I've found one particular case when it doesn't equal nil. It happens when the app is not launched and you do it by deeplinking. In that case, it contains a URL.

 

Interesting! Thanks for the info but it is kind of strage, that this (possibly) one case is still in the AppDelegate

 
Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

Hello, I'm an IOS developer. Recently, when using swift to develop applications, I found that there are few caches written by pure swift. So I wrote a cache -- swiftlycache, a lightweight general-purpose IOS cache library using swift 5. If you are using swift for development, if you also need to use cache, maybe you can try swiftlycache, maybe you will like it, If you like, you can also introduce it to your friends. Thank you
github.com/hlc0000/SwiftlyCache