Add 3D Touch quick actions tutorial

3d touch quick actions tutorial

With the introduction of the iPhone 6S (plus), Apple added a pressure-sensitive layer to their screen.  This creates a bunch of new UX possibilities for creating apps.  It’s possible to do a hard press on an application icon and get shortcuts which take you to a specific point in your app.  For example, if you do a hard-press on the Photo’s app icon you can quickly search for an image, check the most recent images or see your favourites.  It’s also possible to make these quick actions dynamic, meaning that you can add and remove actions based on the state of your application.

3D touch quick actions
3D touch quick actions

In this tutorial I will show you how you can add these quick actions to your application icon.

Define action in Info.plist

The different options which are visible when you press on your application icon are defined in your info.plist file.

First you need to create a new row in your plist and give it the key UIApplicationShortcutItems and set the type to Array. You can now add a dictionary for every quick action.

Select the row you’ve just created and add a new row to the array, set the type to Dictionary.

Within this dictionary you have to add rows with specific keys and values.  You can choose from the following options. The ones with a * in front of the name are required.

  • * UIApplicationShortcutItemType: an unique string you choose to identify inside of your app which action has to be triggered. For example be.thenerd.my-app.create-user
  • * UIApplicationShortcutItemTitle: the string the user sees.  If the string is too long, it will be wrapped on 2 lines if there is no subtitle set (see the next key). If a subtitle is set, the string will be truncated.
  • UIApplicationShortcutItemSubtitle: optional, string which is shown to the user just below the title.
  • UIApplicationShortcutItemIconType: optional, you can use a system-defined icon (the list of keys can be found here)
  • UIApplicationShortcutItemIconFileoptional, the name of the image file in your app bundle or assets catalog. The template files to create your custom icon file can be found here.
  • UIApplicationShortcutItemUserInfooptional, a dictionary with custom data you want to use.
<array>
    <dict>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypeAdd</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Create new user</string>
        <key>UIApplicationShortcutItemType</key>
        <string>be.thenerd.appshortcut.new-user</string>
        <key>UIApplicationShortcutItemUserInfo</key>
            <dict>
             <key>name</key>
             <string>Frederik</string>
            </dict>
    </dict>
</array>
Swift

This will give you the following result.

3D touch quick actions
App shorcut

Get notified on selection

Alright, so how do we get notified when the user selects a shortcut?  After selection the method application:performActionForShortcutItem:completionHandler gets called.  Implement this method in your AppDelegate file.

func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
    
    print("Shortcut tapped")
    
}
Swift

If you tap on the shortcut you’ll see that this message pops up in your console. But how do we know which shortcut has been tapped?

This method has 3 arguments:

  1. application: a reference to the shared UIApplication instance
  2. shortcutItem: a reference to the UIApplicationShortcutItem object
  3. completionHandler: a closure which gets executed when your quick action code completes.

Remember you have specified the UIApplicationShortcutItemType key? With that unique string you can check which action you have to run. To access this string, you just fetch the value of the type property from the shortcutItem argument.

It might be a good idea to create an enum with all possible values, so you don’t make any spelling mistakes!  I’ll create a method handleShortcut:shortcutItem in which you can check what type it is and return a boolean if the action succeeded or not.  You need this because the application:performActionForShortcutItem:completionHandler: method requires that you return a boolean via the completionHandler argument.

func handleShortcut( shortcutItem:UIApplicationShortcutItem ) -> Bool {
    print("Handling shortcut")
    
    var succeeded = false
    
    if( shortcutItem.type == "be.thenerd.appshortcut.new-user" ) {
        
        // Add your code here
        print("- Handling \(shortcutItem.type)")
        
        succeeded = true
        
    }
    
    return succeeded
    
}
Swift

One more thing

There is one caveat … there is a difference between launching your application and going from an inactive state to an active state.

This means you are responsible to make sure that application:performActionForShortcutItem:completionHandler: is called conditionally if for example application:didFinishLaunchingWithOptions: has already handled the application shortcut.

So how can we do this?

We will use a boolean in application:didFinishLaunchingWithOptions: to check if we need to call application:performActionForShortcutItem:completionHandler:.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
  var performShortcutDelegate = true

  ...

}
Swift

Now we’ll have to check if the UIApplicationLaunchOptionsShortcutItemKey is available in the launchOptions dictionary argument.  If this is the case, we will store the instance of UIApplicationShortcutItem in a property so we can reference it later and set our boolean to false, because we don’t want the application:performActionForShortcutItem:completionHandler: to be called again.

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var shortcutItem: UIApplicationShortcutItem?

...
}

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    print("Application did finish launching with options")

    var performShortcutDelegate = true
    
    if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
        
        print("Application launched via shortcut")
        self.shortcutItem = shortcutItem
        
        performShortcutDelegate = false
    }
    
    return performShortcutDelegate
    
}
Swift

Now we can safely implement applicationDidBecomeActive: and check if the shortcutItem property has been set.  If so, the app is not coming from a background state.

func applicationDidBecomeActive(application: UIApplication) {
    print("Application did become active")
        
    guard let shortcut = shortcutItem else { return }
        
    print("- Shortcut property has been set")
        
    handleShortcut(shortcut)
        
    self.shortcutItem = nil
        
}
Swift

And that’s it!

You can find the source code for this project on Github.

Share this post

30 Responses

  1. How to i get the controller which action was clicked? I get nil if i try this on viewDidLoad:
    let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
    print(“At Controller (delegate.shortcutItem?.type)”)
    I am new to iOS development.

    1. You’ll always end up in the AppDelegate’s method when the user used a quick action. Within that method you’ll have to do a check on the type you have specified for the shortcutItem.
      Within that check, you can then create the view controller and present it to the user. I’ve updated the sample project on GitHub, so you can check out the code.

  2. You’ll always end up in the AppDelegate’s method when the user used a quick action. Within that method you’ll have to do a check on the type you have specified for the shortcutItem.
    Within that check, you can then create the view controller and present it to the user. I’ve updated the sample project on GitHub, so you can check out the code.

  3. app crash with error: fatal error: unexpectedly found nil while unwrapping an Optional value

    when i try to edit a UITextField

    any way to fix it

    I’m adding the outlet as:
    @IBOutlet weak var Field: UITextField!

    and

    func handleShortcut( shortcutItem:UIApplicationShortcutItem ) -> Bool {
    print(“Handling shortcut”)
    var succeeded = false
    if( shortcutItem.type == “item.1” ) {
    print(“- Handling (shortcutItem.type)”)
    Field.text == “”
    succeeded = true
    }

    1. Did you check if the Field variable is already initialised. ViewDidLoad hasn’t probably been called yet, so your outlet is still nil which results in a crash if you try to access it at `Field.text`.

      Also, you are not doing an assignment on `Field.text`, but an evaluation because you are using double == characters.

      You’ll first have to get a reference to your view controller, then assign the value you want to pass to the view controller and visualise it in viewDidLoad (or any lifecycle method which get called after that).

      You can check my code on Github where I’ve added a few days ago such an example.

      Regards,
      Frederik

      1. still not working, the app crash every time i try to change the value of the field, i tried viewdidload, applicationdidfinishlaunching, awakefromnib and also tried making a function that gets activated after 5 seconds from taping the quick action and i used the app for a few seconds before the 5 seconds was over then when the action got called the app crashed.

        func handleShortcut( shortcutItem:UIApplicationShortcutItem ) -> Bool {
        print(“Handling shortcut”)
        var succeeded = false
        if( shortcutItem.type == “item.1” ) {
        print(“- Handling (shortcutItem.type)”)
        self.performSelector(“Action”, withObject: nil, afterDelay: 5)
        succeeded = true
        }
        }

        func Action(){
        Field.text = “whatever”
        }

        however when i link a UIButton as an action and use it to change the text in the field the app does not crash

        any ideas

  4. I followed your tutorial but quick actions doesn’t appear in springboard. Should I only set quick actions in plist and manage them in AppDelegate, right? Or there’s something else to do? Thank you!

    1. There must be something wrong in the plist if they are not showing (assuming you are testing on an iPhone 6s)

      Have a look at the example I’ve done here based on Federik’s tutorial: http://i.imgur.com/ufHxSqH.png

      You can leave the icon file, subtitle and type blank to test the shortcut before working on the code to handle them.

      Just make sure the structure for UIApplicationShortcutItems looks identical for you.

        1. Ah I see the issue here. The name of the array.
          You have:
          UIApplicationShortcutItem
          change to
          UIApplicationShortcutItems

  5. The way you have handled:

    var shortcutItem: UIApplicationShortcutItem?

    Works great if the lowest iOS target is 9, but what if we need to support older versions as well? What is the best way to handle this?

    I tried the new @available swift2 checking but on AppDelegate – that results in a crash on every device under ios9. Using @available on performActionForShortcutItem works well though.

    I’m stuck how to handle this 😐

  6. I’ve got another trouble. I had created my two quick actions. Now I want invert their order on screen so I inverted their order in UIApplicationShortcutItems array but app keep displaying quick actions in previous order. I tried cleaning xCode, removing pp from iPhone, ecc but it’s the same…

    1. as far as I know iOS will use the number in the item dictionary to dictate the order. So Item ‘0’ will always prioritize over Item ‘1’ and so on.
      So when you say you inverted the order, did you change the dictionary names to match the order you require?
      Item 1 would become Item 0
      Item 0 would become Item 1

      1. In inverted title, subtitle, icon and type between item 0 and 1 (copy and paste from an item to another…)

    2. If you want to change the order of your quick actions, you just have to change the order of the items inside the UIApplicationShortcutItems array in your plist like Kevin said.

  7. You mentioned, “It’s also possible to make these quick actions dynamic, meaning that you can add and remove actions based on the state of your application”, but didn’t follow-up on how that’s done. Any insight into how to make shortcuts dynamic (different options, icons, etc.)? Thanks!

  8. Yet another amateur question. I’m trying to use 3d Touch for a Spritekite game to start a new game in hard/easy mode. It works when the app wasn’t launched before, but once started, I’m unable to detect if it was started via a shortcut.

    The operation with view controllers is still a close book for me, so within the GameScene.swift I’m getting the status with this command:

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    forceSelection = appDelegate.forceSelection

    At the AppDelegate.swift the selection of the view controller is commented out, because I’ve always got the following error:

    *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Storyboard () doesn’t contain a view controller with identifier ‘Game View Controller”

    Anybody who is able to give me a hint?

    -Stephan-

  9. Here is a question: in iOS 10, any idea how to hide a Today Widget from the home-screen quick action sheet? I can’t find the method…

    1. Figured it out… in the info.plist, implement this key: “Home Screen Widget” with NULL.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts