Skip to Content
Technical Articles

Use Quick Actions to Boost Your User’s Productivity

Introduction

When it comes to business apps, you want to make sure that productivity gets increased through your app. Using Apple’s APIs, device features, as well as OS features, is key to achieving higher productivity.

Let’s take a look at what we could do to increase the productivity for a consultant traveling throughout the year. This consultant has to get reimbursed for all his made expenses during a business trip.

How can this user access the needed functionality as fast and as easy as possible? – The answer is Quick Actions!

In the above-shown screenshot, you can see a Quick Action for showing all available reports, if the user taps on the button, the report list screen of the expenses app will be opened up saving the user manual navigation.

Before we go into the implementation of this Quick Action, feel free to take a look at the official documentation from Apple.

Add Home Screen Quick Actions

Implementation

The app I am using here is generated with the newest version (4.0.9) of the SAP CP SDK for iOS available here.

I am using the Travel Expense Service from my 2019 workshops available here.

Static vs. Dynamic Quick Actions

There are several ways you can implement Quick Actions in iOS. First, we have to understand the difference between static and dynamic Quick Actions.


Static Quick Actions are actions that are visible even before the user has ever started the app because those get added during the compilation of your app. Those should always be actions the user can perform in any state of the app.


Dynamic Quick Actions are different in the way that those actions first get added to the Quick Actions during runtime. The code where you implemented the dynamic action has to be executed first before they become visible.


When it comes to Static Quick Actions you can implement them through the info.plist file, and both action types can also be created via code.Implement a Static Quick Action

In my app project I add a new Quick Action to the info.plist file.

<key>UIApplicationShortcutItems</key>
<array>
    <dict>
        <key>UIApplicationShortcutItemType</key>
        <string>com.sap.example.showReports</string>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypeSearch</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Show Reports</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Show all available Reports</string>
    </dict>
</array>

Add a new array to the info.plist file and set it’s key to UIApplicationShortcutItems.

The UIApplicationShortcutItemType represents a unique key for identification of the shortcut.

Adding a UIApplicationShortcutItemIconType allows you to define an icon. You can use any icon, for information see the the official Apple documentation.

The UIApplicationShortcutItemTitle and UIApplicationShortcutItemSubtitle are there to provide the user information about what this particular shortcut will do.

NOTE: If you’re interested in the available UIApplicationShortcutItemIconTypes you can take a look at the official documentation.

Next in the AppDelegate.swift class we need the following method for handling the user’s interaction with the added shortcut.

application(_:performActionFor:completionHandler:)

This method will get called by the system if the user triggers an interaction with the displayed shortcut. Because there will be more then one shortcut I’ve added an Enum which represents the different shortcut types.

enum ShortcutItemType: String {
    case CreateReport = "com.sap.example.showReports"
}

In the just added app delegate method a distinction of all shortcuts can be made via a simple Switch Statement. In our case we’re not going to perform the action in that method because what we really want is to perform a navigation to the report list as soon as the app becomes active. For that reason an implementation of applicationDidBecomeActive(_:) method.

Important is that you hold an instance of the UIApplicationShortcutItem as a class property.

private var shortcutItemToProcess: UIApplicationShortcutItem?

func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
    // We save it here to later use it to process the navigation.
    shortcutItemToProcess = shortcutItem
}

The implementation of the navigation looks like the following, important to note here is that we have to alter the assistant generated code later on a little bit to make the following code compile.

func applicationDidBecomeActive(_ application: UIApplication) {
    // Get an instance of the Main.storyboard
    let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)

    // This will hold the view controllers
    var masterViewController: UIViewController!
        
    switch shortcutItemToProcess?.type {
    case ShortcutItemType.ShowReport.rawValue:
        logger.debug("Create Report Quick Action tapped.")
        
        // Get an instance of the Report.storyboard and instantiate the ReportMaster view controller    
        let reportStoryBoard = UIStoryboard(name: "Report", bundle: nil)
        let reportMasterViewController = reportStoryBoard.instantiateViewController(withIdentifier: "ReportMaster") as! ReportMasterViewController
        reportMasterViewController.entitySetName = "ReportSet"
        reportMasterViewController.navigationItem.title = "Report"
        masterViewController = reportMasterViewController
        
    default:
        logger.debug("Default case for Quick Action.")
        masterViewController = UIViewController()
    }
    
    // Prepare the navigation and execute it
    let navController = mainStoryBoard.instantiateViewController(withIdentifier: "RightNavigationController") as! UINavigationController
        
    navController.viewControllers = [masterViewController]
    let splitViewController = window!.rootViewController
    if splitViewController is UISplitViewController {
       splitViewController?.showDetailViewController(navController, sender: nil)
    }
        

     // Reset the shortcut item so it's never processed twice.
     shortcutItemToProcess = nil
}

As mentioned before this code won’t compile yet, to make it work additional changes to the ReportMasterViewController.swift class are necessary.

Right now the ReportMasterViewController.swift class gets the data service passed in through navigation, but to make the above code work the data service has to be retrieved in the viewDidLoad(_:).

guard let travelexpenseService = appDelegate.sessionManager.onboardingSession?.odataController.travelexpenseService else {
           AlertHelper.displayAlert(with: "OData service is not reachable, please onboard again.", error: nil, viewController: self)
           return
}

Also add the following computed property to retrieve an instance of the app delegate.

var appDelegate: AppDelegate { return (UIApplication.shared.delegate as! AppDelegate) }

The requestEntities(completionHandler:) method has to be changed to use the data service directly instead of using the generate block.

/*
func requestEntities(completionHandler: @escaping (Error?) -> Void) {
    self.loadEntitiesBlock! { entities, error in
        if let error = error {
            completionHandler(error)
            return
         }
         self.entities = entities!
         completionHandler(nil)
     }
}
*/

func requestEntities(completionHandler: @escaping (Error?) -> Void) {    
    self.travelexpenseService.fetchReportSet { entities, error in
        if let error = error {
            completionHandler(error)
            return
        }
        self.entities = entities!
        completionHandler(nil)
    }
}

With that the shortcut should perform the action from the Quick Actions menu.

Implement a Dynamic Quick Action

Now that we have a static action implemented for showing the list of reports, why not creating a dynamic one for taking a picture of a recipe.

You can add dynamic quick actions from everywhere within the app but in my case I am adding it in the applicationWillResignActive(_:) method of the app delegate.

func applicationWillResignActive(_ application: UIApplication) {
    application.shortcutItems = 
        [UIApplicationShortcutItem(
            type: ShortcutItemType.TakePhoto.rawValue,
            localizedTitle: "Take a Picture",
            localizedSubtitle: nil,
            icon: UIApplicationShortcutIcon(type: .capturePhoto))]
}

Now adding another case to the ShortcutItemType enum.

enum ShortcutItemType: String {
    case ShowReport = "com.sap.example.showReports"
    case TakePhoto = "com.sap.example.takePhoto"
}

Theoretically you could add the necessary code to the application(_:performActionFor:completionHandler:) but we have that switch statement from before.

case ShortcutItemType.TakePhoto:
    logger.debug("Take a picture.")
    let takePictureViewController = mainStoryBoard.instantiateViewController(withIdentifier: "TakePicture") as! TakePictureViewController
    masterViewController = takePictureViewController

In this blog I am not going to show how to implement a view controller opening a camera view. If you’re interested in how to implement that you can take a look here.

Conclusion

Using dynamic or static quick actions can improve the productivity and are easy to implement. Think about actions the user might use more than others or multiple times a day and provide them right there on the Home Screen.

Again the user experience is key!

With that, Happy Coding!

Be the first to leave a comment
You must be Logged on to comment or reply to a post.