Skip to Content

Introduction

During the last days I took a deeper look on the SAP Cloud Platform SDK for iOS, released at the end of March. One topic of interest for me is the provided Onboarding process and functionality, especially the authentication step including the passcode functionality.

The Onboarding Patterns are very well described in the online help for the SDK (compared to other parts of the SDK in the first version). But as I did not just want to read about the functionality I implemented a little example which gave me a better understanding about that specific functionality and in general about the implementation style of the SDK itself.

My example contains a simple view controller with buttons to create a new passcode, to check the set passcode, to change the passcode and to reset the passcode. The reset functionality implements in that case just a removal of the passcode from the Secure Key Value Store.


As the Passcode functionality also supports Touch ID and I do not have deactivated it by coding, Touch ID can be used for authentication on supported devices too.In addition to the functionality executed by pressing one of the buttons, an already set passcode is checked when the app is launched or when it becomes active again after it was suspended.

If you wanna try out the functionality by yourself, you can clone my Github repository https://github.com/pfefferf/sap-cp-sdk-for-ios__passcode-test, add the SAP Cloud Platform SDK for iOS frameworks SAPFoundation, SAPCommon and SAPFiori and run it on your iOS simulator or device. For details about downloading the SDK and adding the frameworks to your Xcode project you can follow the steps in tutorial Installing the SAP Cloud Platform for iOS SDK. The SDK version used for the app is 1.0 SP01 PL02.

Exploring the Passcode Functionality

General

To have a clean coding structure I created a PasscodeHandler class implementation which implements all the “core logic” required for the passcode handling. The class contains logic to read and store the passcode in a Secure Key Value Store, to call the controllers to create, check, change and reset a passcode and it implements the FUIPasscodeControllerDelegate protocol which offers methods to be able to integrate store, check and reset logic for passcodes. The delegate protocol offers also a method “passcodePolicy” which allows to activate specific rules required for the passcode (e.g. it must contain an upper case value, a lower case value, digits, etc.). Details about that can be found in the documentation for the FUIPasscodePolicy struct. In my example case I activated the rule that three of the existing standard rules must be fulfilled (e.g. must have a lower case value, a special sign and a digit). For details about the implementation of the PasscodeHandler please check the PasscodeHandler.swift file in the Github repository.

One point not considered in my example is to implement own passcode validation rules. This is possible by the usage of the FUIPasscodeValidationDelegate protocol. But as the usage is straightforward no one should have problems to implement its own validation logic.

Create a new passcode

For creating a new passcode, the SDK offers the FUIPasscodeCreateController class, which is a sub-class of FUIPasscodeController, which is again a sub-class of UIViewController. Embedded in a UINavigationController the class lets you create a new passcode including a confirmation step. It is automatically detected if the device supports Touch ID. If it is supported the user is asked if Touch ID should be enabled or not for the passcode.

In the examples PasscodeHandler implementation a method “createNewPasscode” is called after pressing the button “Create New Passcode”. This method creates the FUIPasscodeCreateController instance, embeds it into a UINavigationController and presents it. In addition the PasscodeHandler instance is set as delegate for the FUIPasscodeCreateController, so the delegate methods implemented in the PasscodeHandler implementation are called.

func createNewPasscode(_ viewController: UIViewController){
    PassCodeHandler.logger.info("createNewPasscode entered")
        
    let storyboard = UIStoryboard(name: "FUIPasscodeCreateController", 
                       bundle: Bundle(identifier: PassCodeHandler.bundleSAPFiori))
    let vc = storyboard.instantiateViewController( 
               withIdentifier: "PasscodeCreateFirstViewController")
    let passcodeVC = vc as! FUIPasscodeCreateController
    passcodeVC.delegate = self
    let navC = UINavigationController(rootViewController: passcodeVC)
    viewController.present(navC, animated: true, completion: {})
 }

After a new passcode is entered and confirmed the FUIPasscodeControllerDelegate method “shouldTryPasscode” is called with input mode FUIPasscodeInputMode.create. The handler reacts on that by saving the passcode in the Secure Key Value Store and by dismissing the FUIPasscodeCreateController.

public func shouldTryPasscode(_ passcode: String, forInputMode inputMode: FUIPasscodeInputMode, fromController passcodeController: FUIPasscodeController) throws {
    switch inputMode {
    case .create:
        try self.savePasscode(passcode)
        passcodeController.dismiss(animated: true, completion: {})
        FUIToastMessage.show(message: NSLocalizedString("Passcode successfully created.",
          comment: "Success message when passcode was created."), withDuration: 1)
    
    ... 

    }
}

Check an existing passcode

After a passcode is set the sample application offers the option to check the passcode by pressing button “Check Passcode”. For the check the FUIPasscodeInputController class implementation is used, which provides a view on which the passcode can be entered. If Touch ID is supported and enabled also the Touch ID check is done automatically. The PasscodeHandler method “checkExistingPasscode” instantiates the controller and displays it.

func checkExistingPasscode(_ viewController: UIViewController, animated: Bool = true){
    PassCodeHandler.logger.info("checkExistingPasscode entered")
        
    let storyboard = UIStoryboard(name: "FUIPasscodeInputController", 
                       bundle: Bundle(identifier: PassCodeHandler.bundleSAPFiori))
    let vc = storyboard.instantiateViewController(
               withIdentifier: "PasscodeInputViewController")
    let passcodeVC = vc as! FUIPasscodeInputController
    passcodeVC.delegate = self
    passcodeVC.isToShowCancelBarItem = false
    let navC = UINavigationController(rootViewController: passcodeVC)
    viewController.present(navC, animated: animated, completion: {})
}

After a passcode is entered or determined via Touch ID, the delegate method “shouldTryPasscode” is called with input mode FUIPasscodeInputMode.match. The implementation in my example uses a check method to check if the entered passcode matches the existing passcode. In case of success, the FUIPasscodeInputController is dismissed. In case of an error, an exception is thrown on which the FUIPasscodeInputController reacts by displaying a message (FUIPasscodeController.retryPasscodeMessageString) that the passcode is not valid and that the user should try again to enter it.

public func shouldTryPasscode(_ passcode: String, forInputMode inputMode: FUIPasscodeInputMode, fromController passcodeController: FUIPasscodeController) throws {        
    switch inputMode {

    ...

    case .match:
        if self.isPasscodeValid(passcode){
            passcodeController.dismiss(animated: true, completion: {})
            FUIToastMessage.show(message: NSLocalizedString("Passcode successfully validated.",
              comment: "Success message when passcode was validated."), withDuration: 1)
        }else{
            throw PassCodeHandler.errorConsts.passcodeNotValid
        }

    ...

    }
}

Change an existing passcode

By pressing the button “Change Passcode” in the example application the change passcode process is triggered which uses the FUIPasscodeChangeController class. The standard implementation asks the user to enter the current passcode and if that matches to enter and confirm a new passcode. It has to be considered that the current passcode cannot be determined by Touch ID (even if activated) in the change process.

Pressing button “Change Passcode” calls method “changePasscode” of the PasscodeHandler implementation which presents the FUIPasscodeChangeController then.

 func changePasscode(_ viewController: UIViewController){
     PassCodeHandler.logger.info("changePasscode entered")
        
     if let changeController = FUIPasscodeChangeController.createInstanceFromStoryboard() {
         changeController.passcodeControllerDelegate = self
         viewController.present(changeController, animated: true, completion: {})
     }
 }

The delegate method “shouldTryPasscode” is called twice in the change process. One time with input mode FUIPasscodeInputMode.matchForChange to be able to check if the entered current passcode matches the set passcode and one time with input mode FUIPasscodeInputMode.change to be able to store the new passcode and dismiss the FUIPasscodeChangeController. The implementation for both input modes in my PasscodeHandler looks like following. For input mode matchForChange the entered current passcode is checked against the current existing stored passcode. If the validation fails, an exception is thrown to indicate to the FUIPasscodeChangeController that the entered current passcode did not match. For input mode change the entered new passcode is saved and the FUIPasscodeChangeController is dismissed.

public func shouldTryPasscode(_ passcode: String, forInputMode inputMode: FUIPasscodeInputMode, fromController passcodeController: FUIPasscodeController) throws {
  
    switch inputMode {

    case .change:
        try self.savePasscode(passcode)
        passcodeController.dismiss(animated: true, completion: {})
        FUIToastMessage.show(message: NSLocalizedString("Passcode successfully changed.", comment: "Success message when passcode was changed."), withDuration: 1)

    case .matchForChange:
        if !self.isPasscodeValid(passcode){
            throw PassCodeHandler.errorConsts.passcodeNotValid
        }
    }
}

Reset an existing passcode

As described above my passcode reset functionality just removes an existing passcode from the Secure Key Value Store and allows to set a new passcode by the user. In real world scenarios this is of course a much more sophisticated process including e.g. an authentication process to validate the authenticity of the user requested the passcode reset.

Pressing the button”Reset Passcode” on the examples view controller, calls the method “resetPasscode” of the PasscodeHandler.

func resetPasscode() throws {
    PassCodeHandler.logger.info("resetPasscode entered")
        
    do{
        _ = try self.store.remove(PassCodeHandler.passcodeKey)
        self.passcode = nil
            
        FUIToastMessage.show(message: NSLocalizedString("Passcode successfully reset.", 
          comment: "Success message when passcode was reset."), withDuration: 1)
    }catch let error{
        PassCodeHandler.logger.error(error.localizedDescription)
        throw error
    }
}

If the reset button of a FUIPasscodeController* implementation is called, method “shouldResetPasscode” of the FUIPasscodeControllerDelegate protocol implemented in the PasscodeHandler is called. This method calls the “resetPasscode” method of the PasscodeHandler and dismisses the current active FUIPasscodeController.

public func shouldResetPasscode(fromController passcodeController: FUIPasscodeController) {
    do{
        try self.resetPasscode()
        passcodeController.dismiss(animated: true, completion: {})
    }catch let error{
        PassCodeHandler.logger.error(error.localizedDescription)
    }
}

Conclusion

From my opinion it is very easy to use the SDK provided passcode functionalities after a general understanding of the SDK itself is reached.

The passcode controllers provide a lot of configuration options not described in that post, which allow to configure the look and behavior of the passcode functionality according to specific needs (for the configuration options check the documentation for the FUIPasscodeController class and its sub-classes).

For all those, who do not want to make their hands dirty with coding, I prepared a little video showing the passcode functionality in action. Please forgive me, there are some German texts on standard controls because I did not switch my phones language to English during recording (but that is a good chance to use a translator and learn some German words :-)). But even with that German words it is understandable by everyone I think.

.

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Michael Svith Thøgersen

    Hi,

    Nice and well described blog… one thing that I think is missing is that passcode policy “should” be downloaded using the SCP Mobile Platform settings (service) and not hardcoded but thumbs up for the blog 🙂

    /Michael

    (0) 
    1. Florian Pfeffer Post author

      For sure in Enterprise apps which will be configured/adapted by several customers such kind of settings are configured in a configuration stored on the server which is downloaded by the app using the Configuration Provider functionality of the SDK. But nevertheless the settings have to be applied using a FUIPasscodePolicy or in an own Passcode validation rule using the provided delegate methods.

      (0) 

Leave a Reply