Search code examples
iosobjective-ciphoneios93dtouch

3D Touch UIapplicationShortcut doesn't work when app is closed


Hi and thanks in advance.

I have an application i wrote that uses the new UIApplicationShortcut's on the home screen using 3D touch. The shortcuts work once the app is app, but if the app is closed out the shortcuts do not work on the home screen.

When the application is closed the shortcuts launch the app, but the shortcuts do not work, only the application launches.

here is the two methods I use for the shortcuts.

- (void)setupViewControllers {
    // setup the navigation stack

    CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
    if (iOSDeviceScreenSize.height == 480)
    {
        _navigationController = (UINavigationController *)self.window.rootViewController;

        // iPhone 5 and iPod Touch 5th generation: 4 inch screen
        // Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone4
        UIStoryboard *iPhone4Storyboard = [UIStoryboard storyboardWithName:@"Main4s" bundle:nil];

        UIViewController *initialViewController = [iPhone4Storyboard instantiateInitialViewController];
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

        self.window.rootViewController  = initialViewController;

        self.myViewController = (ViewController *)_navigationController.topViewController;
        self.myViewController.managedObjectContext = self.managedObjectContext;

        [self.window makeKeyAndVisible];
    }

    if (iOSDeviceScreenSize.height == 568)
    {
        _navigationController = (UINavigationController *)self.window.rootViewController;

        // iPhone 5 and iPod Touch 5th generation: 4 inch screen
        // Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone4
        UIStoryboard *iPhone4Storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

        UIViewController *initialViewController = [iPhone4Storyboard instantiateInitialViewController];
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

        self.window.rootViewController  = initialViewController;

        self.myViewController = (ViewController *)_navigationController.topViewController;
        self.myViewController.managedObjectContext = self.managedObjectContext;

        [self.window makeKeyAndVisible];
    }

    if (self.myViewController){NSLog(@"myViewController is not nil, setupViewControllers");}
    else
    {
        NSLog(@"myViewController is nil, setupViewControllers");//breakpoint inserted here...
    }
}

- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {

    [self setupViewControllers];

    // react to shortcut item selections
    NSLog(@"A shortcut item was pressed. It was %@.", shortcutItem.localizedTitle);
    if([shortcutItem.localizedTitle isEqualToString:@"Scan Tag"])
    {
        [self.myViewController toggleScanningTapped:nil];
    }
    if([shortcutItem.localizedTitle isEqualToString:@"Enter Tag"])
    {
        [self.myViewController manualEntry];
    }
    if([shortcutItem.localizedTitle isEqualToString:@"Search for a Tag"]){
        [self.myViewController SearchSwitch];
        [self.myViewController toggleScanningTapped:nil];
    }
    if([shortcutItem.localizedTitle isEqualToString:@"Just Scan & Copy"]){
        [self.myViewController justCopy];
    }

    if (self.myViewController){NSLog(@"myViewController is not nil, performActionForShortcutItem");}
    else
    {
        NSLog(@"myViewController is nil, performActionForShortcutItem");
    }
}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    [self setupViewControllers];

    NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
    if (url != nil && [url isFileURL]) {
        [self.myViewController handleOpenURL:url];
    }

    if (self.myViewController){NSLog(@"myViewController is not nil, didFinishLaunchingWithOptions");}
    else{NSLog(@"myViewController is nil, didFinishLaunchingWithOptions");}

    return YES;
}

here is the plist entries...

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>UIApplicationShortcutItemType</key>
        <string>com.PALIANTech.SUNYScanner.static1</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Scan Tag</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Add tag to DB</string>
        <key>UIApplicationShortcutItemIconFile</key>
        <string>barcode.png</string>
    </dict>
    <dict>
        <key>UIApplicationShortcutItemType</key>
        <string>com.PALIANTech.SUNYScanner.static2</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Enter Tag</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Manually add tag to DB</string>
        <key>UIApplicationShortcutItemIconFile</key>
        <string>ManualEntry.png</string>
    </dict>
    <dict>
        <key>UIApplicationShortcutItemType</key>
        <string>com.PALIANTech.SUNYScanner.static3</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Search for a Tag</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Scan a tag to search for</string>
        <key>UIApplicationShortcutItemIconFile</key>
        <string>search.png</string>
    </dict>
</array>
</plist>

Any help or suggestions would be greatly appreciated.


Solution

  • The reason is that when your app starts freshly self.myViewController is still nil at the time when you check your short cut items in performActionForShortcutItem and didFinishLaunchingWithOptions. So nothing happens.

    To fix this you would have to instantiate myViewController and add it to the navigation stack when the app is opened via a shortcut.

    EDIT

    Here is a (stripped down) version that works:

    AppDelegate

    @interface AppDelegate ()
    @property ViewController *viewController;
    @end
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        [self setupViewControllers];
    
        return YES;
    }
    
    - (void)setupViewControllers {
        self.viewController = [[ViewController alloc] init];
        self.viewController.view.backgroundColor = [UIColor lightGrayColor];
        self.viewController.label.text = @"Normal Launch";
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
    
        self.window.rootViewController  = navigationController;
        [self.window makeKeyAndVisible];
    }
    
    - (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
        [self setupViewControllers];
    
        if([shortcutItem.localizedTitle isEqualToString:@"Scan Tag"]) {
            self.viewController.label.text = @"Shortcut: Scan Tag";
        }
        if([shortcutItem.localizedTitle isEqualToString:@"Enter Tag"]) {
            self.viewController.label.text = @"Shortcut: Enter Tag";
        }
        if([shortcutItem.localizedTitle isEqualToString:@"Search for a Tag"]) {
            self.viewController.label.text = @"Shortcut: Search Tag";
        }
        if([shortcutItem.localizedTitle isEqualToString:@"Just Scan & Copy"]) {
            self.viewController.label.text = @"Shortcut: Scan & Copy Tag";
        }
    }
    
    @end
    

    The ViewController class is a plain UIViewController subclass that just has a label for demonstration purposes.

    I have tested the shortcuts with an app that was in the background and with the app completely removed from memory. Both worked as intended.

    UPDATE

    If you are using a ViewController from a storyboard you have to initialize it like this:

    - (void)setupViewControllers {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        self.viewController = (ViewController *)[storyboard instantiateViewControllerWithIdentifier:@"ViewController"];
        ...
    }
    

    Don't forget to set the ViewController's identifier in your storyboard:

    enter image description here