iosobjective-ciphonexcode3dtouch

Static Quick Actions not working in objective c iOS


I am very new to the iOS Application development and I have been following tutorials to build my career . So starting with I am trying to Implement Static Quick Actions in Objective C as per client project . I was able to add the keys and got the options while pressing the icon . But when I press the Quick Actions nothing happens and it just launches the Application. I have researched a lot and I tried but in vain. Below is the Appdelegate code which I have written:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    BOOL shouldPerformAdditionalDelegateHandling = YES;
    
    
    // Check API availiability
    // UIApplicationShortcutItem is available in iOS 9 or later.
    if([[UIApplicationShortcutItem class] respondsToSelector:@selector(new)]){
        NSLog(@"HERE");
        //  [self configDynamicShortcutItems];
        
        // If a shortcut was launched, display its information and take the appropriate action
        UIApplicationShortcutItem *shortcutItem = [launchOptions objectForKeyedSubscript:UIApplicationLaunchOptionsShortcutItemKey];
        
        if(shortcutItem)
        {
            NSLog(@"shortcut launch");
            // When the app launch at first time, this block can not called.
            
            [self handleShortCutItem:shortcutItem];
            
            // This will block "performActionForShortcutItem:completionHandler" from being called.
            shouldPerformAdditionalDelegateHandling = NO;
            
            
        }else{
            NSLog(@"normal launch");
            // normal app launch process without quick action
            
            // [self launchWithoutQuickAction];
            
        }
        
    }else{
        
        // Less than iOS9 or later
        
        //  [self launchWithoutQuickAction];
        
    }
    
    
    return shouldPerformAdditionalDelegateHandling;
}


- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler{
    NSLog(@"performActionForShortcutItem");
    
    BOOL handledShortCutItem = [self handleShortCutItem:shortcutItem];
    
    completionHandler(handledShortCutItem);
}


/**
 *  @brief handle shortcut item depend on its type
 *
 *  @param shortcutItem shortcutItem  selected shortcut item with quick action.
 *
 *  @return return BOOL description
 */
- (BOOL)handleShortCutItem : (UIApplicationShortcutItem *)shortcutItem{
    
    BOOL handled = NO;
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    
    
    if ([shortcutItem.type isEqualToString:@"PlayMusic"]) {
        handled = YES;
        NSLog(@"Play");
        
        secondview *vc = [storyboard instantiateViewControllerWithIdentifier:@"second"];
        
        self.window.rootViewController = vc;
        
        [self.window makeKeyAndVisible];
        
    }
    
    else if ([shortcutItem.type isEqualToString:@"StopMusic"]) {
        handled = YES;
        NSLog(@"Stop");
        //        ThirdViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"thirdVC"];
        //
        //        self.window.rootViewController = vc;
        
        [self.window makeKeyAndVisible];
    }
    
    
    return handled;
}

However I can see the Quick Action items when I deep press the home icon but on clicking that no action happens. I check the key in info.plist and its the same . Quick Actions

Below is the info.plist for adding Static Actions

<key>UIApplicationShortcutItems</key>
<array>
    <dict>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypePlay</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Play</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Start playback</string>
        <key>UIApplicationShortcutItemType</key>
        <string>PlayMusic</string>
    </dict>
    <dict>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypePlay</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>STOP</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Stop playback</string>
        <key>UIApplicationShortcutItemType</key>
        <string>StopMusic</string>
    </dict>

Can some one please help where I am doing wrong?


Solution

  • since iOS(13.0, *) AppDelegate handles its own UIWindow via SceneDelegate's and so the method

    @implementation AppDelegate
    
    -(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
    {
        // here shortcut evaluation below ios(13.0)
    }
    
    @end
    

    is only invoked on iPhones or iPads with iOS lower Version 13.0

    So to make it work in iOS(13.0,*) and above you need to implement it in SceneDelegate with a slightly different name, see below.. (matt was right!)

    @implementation SceneDelegate
    
    -(void)windowScene:(UIWindowScene *)windowScene performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
        // here shortcut evaluation starting with ios(13.0)
        NSLog(@"shortcutItem=%@ type=%@",shortcutItem.localizedTitle, shortcutItem.type);
    }
    
    @end
    

    PS: The Apple Example Code for Quick Actions in swift is bogus when you assume you can use it as is in iOS(13.0,*) because it is written for versions earlier iOS < 13.0

    EDIT: as asked, here in full beauty for iOS >= 13.0

    @interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
    @property (strong, nonatomic) UIWindow * window;
    @end
    

    and

    -(void)windowScene:(UIWindowScene *)windowScene performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
        /* //go to Info.plist open as Source Code, paste in your items with folling scheme
        <key>UIApplicationShortcutItems</key>
        <array>
            <dict>
                <key>UIApplicationShortcutItemIconType</key>
                <string>UIApplicationShortcutIconTypePlay</string>
                <key>UIApplicationShortcutItemTitle</key>
                <string>Play</string>
                <key>UIApplicationShortcutItemSubtitle</key>
                <string>Start playback</string>
                <key>UIApplicationShortcutItemType</key>
                <string>$(PRODUCT_BUNDLE_IDENTIFIER).Start</string>
            </dict>
            <dict>
                <key>UIApplicationShortcutItemIconType</key>
                <string>UIApplicationShortcutIconTypePlay</string>
                <key>UIApplicationShortcutItemTitle</key>
                <string>STOP</string>
                <key>UIApplicationShortcutItemSubtitle</key>
                <string>Stop playback</string>
                <key>UIApplicationShortcutItemType</key>
                <string>$(PRODUCT_BUNDLE_IDENTIFIER).Stop</string>
            </dict>
        </array>
         */
        
        // may look complex but this is 100% unique
        // which i would place $(PRODUCT_BUNDLE_IDENTIFIER). in front of types in Info.plist
        NSString *devIdent = [NSString stringWithFormat:@"%@.",NSBundle.mainBundle.bundleIdentifier];
        
        if ([shortcutItem.type isEqualToString:[devIdent stringByAppendingString:@"Start"]]) {
            completionHandler([self windowScene:windowScene byStoryBoardIdentifier:@"startview"]);
            
        } else if ([shortcutItem.type isEqualToString:[devIdent stringByAppendingString:@"Stop"]]) {
            completionHandler([self windowScene:windowScene byStoryBoardIdentifier:@"stopview"]);
    
        } else {
            NSLog(@"Arrgh! unrecognized shortcut '%@'", shortcutItem.type);
        }
        //completionHandler(YES);
    }
    
    -(BOOL)windowScene:(UIWindowScene *)windowScene byStoryBoardIdentifier:(NSString *)identifier {
        // idear is to return NO if build up fails, so we can startup normal if needed.
        UIStoryboard *story = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        if (story!=nil) {
            UIViewController *vc = [story instantiateViewControllerWithIdentifier:identifier];
            if (vc!=nil) {
                if (_window==nil) _window = [[UIWindow alloc] initWithWindowScene:windowScene];
                _window.rootViewController = vc;
                
                // .. OR following code..
                //[_window.rootViewController presentViewController:vc animated:YES completion:^{ }];
                //[_window makeKeyAndVisible];
                
                // should be success here..
                return YES;
            }
        }
        // no success here..
        return NO;
    }
    

    You can do also other things than instantiate a ViewController from Storyboard. In example you could switch a property or something similar.