28

I am trying to use core spotlight to open a view controller from the spotlight search results.

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler 
{

if(self.window.rootViewController){
    [self.window.rootViewController restoreUserActivityState:userActivity];
}

return YES;
}

This seems to work when the app is already running in background, however when it is closed and I tap on the spotlight search result it seems that this method gets not called and the behavior I get is that my application simply starts in the main interface.

Do you have any suggestion for making it work also when my app is closed? Is there a way to debug what is happening (since I need to run the app to get the debugger attached I don't know how to simulate the app opening from the search result)?.

Niko Zarzani
  • 1,372
  • 2
  • 15
  • 28

8 Answers8

55

Niko,

first of all: there's a way to start your app from Xcode and not opening it immediately: open your scheme properties, go to the "run" section, and under "info", there's a switch that will help you to debug what's happening:

"Wait for executable to be launched".

If you activate this switch, you can launch the app from Xcode, Xcode will wait until the app is opened from search and then it will attach the debugger to it.

Hope that helps!

Ivan

Ivan Oliver
  • 551
  • 3
  • 2
42

In the new Swift 5 there is a new new file called SceneDelegate.swift. Use the method scene(_ scene: UIScene, continue userActivity: NSUserActivity)

adam eliezerov
  • 2,185
  • 2
  • 11
  • 17
23

If you are using the Facebook SDK, and in didfinishlaunching your are returning FBSDK, instead of plain text, and returning true at the end, it can cause problems hitting continueuseractivity.

After searching a lot, and trying different ways, I just had to return true and comment this:

FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)

Michelle Welcks
  • 3,513
  • 4
  • 21
  • 34
hopye2
  • 231
  • 1
  • 2
11

continue userActivity is changed in the latest swift version. Changing the func worked for me.

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

to

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool.
Arnab
  • 4,216
  • 2
  • 28
  • 50
10

didFinishLaunchingWithOptions needs to return YES so continueUserActivity will be called.

Add to End of application: didFinishLaunchingWithOptions:

NSDictionary *activityDictionary = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (activityDictionary) {
    NSUserActivity *userActivity = activityDictionary[UIApplicationLaunchOptionsUserActivityTypeKey];
    if (userActivity) {
        return YES;
    }
}

return NO;
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
cbartel
  • 1,298
  • 1
  • 11
  • 18
  • 4
    `willFinishLaunchingWithOptions` *also* needs to return `YES` – MeXx Mar 08 '18 at 16:50
  • thx @MeXx I lost lot of time before to find your comment, and willFinishLaunchingWithOptions was required to return YES for me too! – Chris Jul 06 '19 at 17:21
8

If the app was closed, application: continueUserActivity: is not called. Instead, you get all the information launchOptions dictionnary in application: didFinishLaunchingWithOptions:

// In application: didFinishLaunchingWithOptions:
NSDictionary *activityDic = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];  
if (activityDic) {
    // Continue activity here
}
7

If your app uses Scenes, make sure you have implemented both delegates:

this one for the case when the app is not running at the moment of clicking the link:

   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
      // the url is expected to be in connectionOptions.userActivities.first?.webpageURL in case if its type is NSUserActivityTypeBrowsingWeb
   }

AND this one for the case when the app is in background or in foreground:

   func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
       // the url is going to be in userActivity.webpageURL
   }

If your app does NOT use Scenes:

Double-check this method call carefully, because it has changed some time ago and legacy projects can still have old code:

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

This is the call which worked in my case:

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

(Don't be deceived by Xcode's warning suggesting to add private - it won't help)

Check Apple's documentation about Universal Links for more details.

Vitalii
  • 4,267
  • 1
  • 40
  • 45
4

Here it follows the complete answer following your advices.

// In application: didFinishLaunchingWithOptions:
NSDictionary *activityDic = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];

if (activityDic) {
    if(self.window.rootViewController){
        NSUserActivity * userActivity = [activityDic valueForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
        [self.window.rootViewController restoreUserActivityState:userActivity];
    }
}
Niko Zarzani
  • 1,372
  • 2
  • 15
  • 28