Skip to Content
Technical Articles

Getting Started with the SAP Cloud Platform SDK for iOS – Part 2 – Registration

Previous (Setup)   Home   Next (Logging)

A first step in an enterprise application is to have the app onboard or register against an app configuration defined in the SAP Cloud Platform Mobile Services. SAPFioriFlows provide an API to simplify this process which includes a welcome screen, a registration screen, a EULA screen, as well as app protection using a passcode, Touch ID or Face ID.

The following are some additional sources of documentation on SAPFioriFlows.
SAPFioriFlows API Reference
On-boarding Flow Recommendations
Authentication with Native iOS Mobile Interactive Tutorial

The following steps add the logic to onboard or register using basic authentication with the SAP Cloud Platform Mobile Services the first time the app starts. The second time the app starts, the saved credentials will be used so the user does not have to re-enter them.

  1. Right click on the parent folder of ViewController.swift (GettingStarted) and create a new file with the template Property List. Choose New File, then scroll down to the Resource section and select Property List. Name it ConfigurationProvider.plist and click Create.
  2. Right click on the ConfigurationProvider.plist file and choose Open As, Source Code.
    Place the below contents into the file.

    <?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">
        <dict>
            <key>auth</key>
            <array>
                <dict>
                    <key>config</key>
                    <dict/>
                    <key>type</key>
                    <string>basic.default</string>
                </dict>
            </array>
            <key>host</key>
            <string>hcpms-XXXXXXtrial.hanatrial.ondemand.com</string>
            <key>protocol</key>
            <string>https</string>
            <key>appId</key>
            <string>com.iossdk.gs</string>
            <key>authPath</key>
            <string>com.sap.edm.sampleservice.v2</string>
        </dict>
    </plist>​
  3. Replace the XXXXXX value in host so it is correct for your SAP Cloud Platform Mobile Services server. The value can be seen on the APIs tab of the application.
  4. Add the following import to the other imports at the top of ViewController.
    import SAPFioriFlows
    
  5. In ViewController add the following under the declaration for numberOfPresses.
    let presentationDelegate = ModalUIViewControllerPresenter()
    var myContext: OnboardingContext!
    var appId: String = ""
    var authPath: String = ""
    var serviceURL: URL = URL.init(string: "http://empty.org")!
    
    //Steps executed during Onboarding
    private var onboardingSteps: [OnboardingStep] {
        return [
            self.configuredWelcomeScreenStep(),
            SAPcpmsSessionConfigurationStep(),
            BasicAuthenticationStep(),
            SAPcpmsSettingsDownloadStep(),
            SAPcpmsLogSettingsApplyStep(),
            self.configuredStoreManagerStep(),
        ]
    }
    
    //Steps executed during Restoring
    private var restoringSteps: [OnboardingStep] {
        return [
            self.configuredStoreManagerStep(),
            self.configuredWelcomeScreenStep(),
            SAPcpmsSessionConfigurationStep(),
            BasicAuthenticationStep(),
            SAPcpmsSettingsDownloadStep(),
            SAPcpmsLogSettingsApplyStep(),
        ]
    }​
  6. Add the following functions to ViewController.swift
    func configuredWelcomeScreenStep() -> WelcomeScreenStep {
        let discoveryConfigurationTransformer = DiscoveryServiceConfigurationTransformer(applicationID: appId, authenticationPath: authPath)
    
        //use the values from ConfigurationProvider.plist
        let welcomeScreenStep = WelcomeScreenStep(transformer: discoveryConfigurationTransformer, providers: [FileConfigurationProvider()])   
        
        welcomeScreenStep.welcomeScreenCustomizationHandler = { welcomeScreen in
            welcomeScreen.isDemoModeAvailable = false
            welcomeScreen.headlineLabel.text = "Getting Started with the SAP Cloud Platform SDK for iOS"
            welcomeScreen.detailLabel.text = "Use this app to explore the SAP Cloud Platform SDK for iOS"
            welcomeScreen.primaryActionButton.titleLabel?.text = "Start"
        }
        return welcomeScreenStep
    }
    
    func configuredStoreManagerStep() -> StoreManagerStep {
        let step = StoreManagerStep()
        //If passcode policy is disabled on the server and the next line is uncommented, the app won’t be protected with a passcode
        //step.defaultPasscodePolicy = nil
        return step
    }
    
    func onboardOrRestore() {
        myContext = OnboardingContext(presentationDelegate: presentationDelegate)
        
        //If we previously onboarded
        if let savedUUIDString = UserDefaults.standard.string(forKey: "userOnboardingID"), let uuid = UUID(uuidString: savedUUIDString) {
            myContext.onboardingID = uuid
            OnboardingFlowController.restore(on: restoringSteps, context: myContext) { result in
                print("and the result is ...")
                switch result {
                case let .success(context):
                    print("Successfully restored")
                    self.myContext = context
                    //get host port from registration rather than hardcoded in plist with Discovery Service
                    //does not work when offline
                    //self.serviceURL = context.authenticationURL!
                case let .failed(error):
                    print("Failed to restore: \(error)")
                    if (error.localizedDescription == "Passcode reset!") {
                        self.unregister()
                    }
                }
            }
        }
        //First time the app is opened so perform the onboarding flow
        else {
            OnboardingFlowController.onboard(on: onboardingSteps, context: myContext) { result in
                print("and the result is ...")
                switch result {
                case let .success(context):
                    self.myContext = context
                    //get host port from registration rather than hardcoded in plist with Discovery Service
                    //does not work when offline
                    //self.serviceURL = context.authenticationURL!
                    UserDefaults.standard.set(context.onboardingID.uuidString, forKey:"userOnboardingID")
                    print("Successfully onboarded")
                case let .failed(error):
                    print("Failed to onboard, retrying due to: \(error)")
                    URLCache.shared.removeAllCachedResponses()
                    HTTPCookieStorage.shared.removeCookies(since: Date.distantPast)
                    self.onboardOrRestore()
                }
            }
        }
    }
    
    func unregister() {
        OnboardingFlowController.reset(on: self.restoringSteps, context: myContext) {
            URLCache.shared.removeAllCachedResponses()
            HTTPCookieStorage.shared.removeCookies(since: Date.distantPast)
            UserDefaults.standard.set(nil, forKey:"userOnboardingID")
            self.onboardOrRestore()
        }
    }
    
    func getAppConfig() {
        var host = ""
        if let path = Bundle.main.path(forResource: "ConfigurationProvider", ofType: "plist") {
            let myDict = NSDictionary(contentsOfFile: path)
            appId = myDict?.object(forKey: "appId") as! String
            authPath = myDict?.object(forKey: "authPath") as! String
            host = myDict?.object(forKey: "host") as! String
        }
        serviceURL = URL.init(string: "https://\(host)/\(authPath)")!
    }​
  7. At the end of viewDidLoad function, start the onboarding process.
    getAppConfig()
    UINavigationBar.applyFioriStyle()
    onboardOrRestore()
  8. Run the app and the following screens are shown.
    Note the LaunchScreen is shown first but it is an empty screen by default.
    Welcome Screen

    Basic authentication screen

    Touch ID or Face ID screen
    Not Now was pressed. The Touch ID option can be enabled via the simulator’s Hardware,

    Touch ID or Face ID, Enrolled menu.

    Passcode screen
    This screen is shown if the device does not support Touch ID or Face ID or the Not Now button was pressed.

    By default, a Passcode Policy specifying that 8 characters must be entered is enforced.
    The Passcode Policy for the app can be managed by the Client Policy in the SAP Cloud Platform Mobile Services management cockpit for new registrations.
    Note, next time the app opens, just the passcode screen will be shown and the previously entered credentials will be used to fulfill any authentication challenges to the SAP Cloud Platform Mobile Services server.

    Note, if your app does not need a passcode screen, it can be disabled by unchecking the Enable Passcode Policy and uncommenting the below line in the method configuredStoreManagerStep.

    step.defaultPasscodePolicy = nil
    
  9. Notice that following a registration, the management cockpit will show the application’s registrations.
    Registrations that have not been used after a specified time can be automatically deleted. See the Automatic Removal section of the previous screen shot.
  10. This step is optional.
    In the above registration, the values for SAP Mobile Services server host and port are taken from the ConfigurationProvider.plist file that is part of the application. Another option to provide these values is the SAP Discovery Service or from a Mobile Device Management solution. See also Enabling Applications to Discover Configurations and App Configuration Using a Discovery Service Provider.  To try this out using the SAP Discovery Service follow the below steps.
    In the function configuredWelcomeScreenStep, replace FileConfigurationProvider with DiscoveryServiceConfigurationProvider and pass in the appId.

    //use values published in the discovery service.  
    let welcomeScreenStep = WelcomeScreenStep(transformer: discoveryConfigurationTransformer, providers: [DiscoveryServiceConfigurationProvider(applicationID: appId)])
    

    To publish the configuration, click Publish to Discovery Service.

    Notice below that the supplied domains match one of the configured domains in the SAP Discovery Service.

  11. Note, that a JSON string containing the published configuration can be seen by making a request in the browser as shown below.

    https://discovery.sapmobilesecure.com/config-api.svc/ApplicationConfigurations/getApplicationConfiguration(AppConfigID='com.iossdk.gs:1.0',EmailAddress='dan@trial-pXXXXXXXtrial.sapmobileplace.com')

Previous (Setup)   Home   Next (Logging)

13 Comments
You must be Logged on to comment or reply to a post.
  • Hi Dan,

    Nice Blog.!! I was wondering is there a way to remove Welcome Screen and Passcode screen and directly present user with login page as we do with Kapsel?

    • In the onboardingSteps, I believe you can remove the line
      self.configuredWelcomeScreenStep(),

      To remove the passcode screen, search in the blog above for
      step.defaultPasscodePolicy = nil

      Thanks for the feedback,
      Dan

      • Thanks for response Dan.! I had tried commenting the “Self.ConfiguredWelcomeScreenStep()” but it result into following error .

         

        FAILED (Error: Missing argument in SAPcpmsSessionConfigurationStep: SAPcpmsSettingsParameters)

        and the result is …

        Failed to onboard, retrying due to: Missing argument in SAPcpmsSessionConfigurationStep: SAPcpmsSettingsParameters

        2018-03-07 12:04:36.258949+0530 FioriTest1[95502:6562779] [general] # SAP.Flows.Onboarding.Steps.Configurators.SAPcpms – ERROR – (Main) – SAPcpmsSessionConfigurationStep.swift.onboard(context:completionHandler:) – Onboard: Could not find `SAPcpmsSettingsParameters` under the infoKey.

        2018-03-07 12:04:36.259249+0530 FioriTest1[95502:6562779] [general] # SAP.Flows.Onboarding.FlowController – ERROR – (Main) – OnboardingFlowController.swift.runMethod(index:context:) – Step (1): SAPcpmsSessionConfigurationStep

        I

    • In my nested response, I’ve linked to a solution of your problem.

      However, I also felt I should mention that this welcome step is not only pure UI. It also includes logic to load the bootstrap configuration from external sources, eg QR code, discovery service, or internal plist file. Once you don’t have that, you obviously need to find that configuration via other means

      Thanks
      Andreas

  • Hey Dan,

    it would be great when we could add also an additional image on top of the BasicAuthenticationStep (or at least on every screen of the onboarding flow). is or will this possible in the future?

    Regards,
    Chris

     

  • Hi Dan,

    Thanks for this great tutorial, but I have a problem: When I press on the start button, I always get the error “Thread 1: EXC_BAD_ACCESS (code=1, address=XXXXX)”.

    The same happens with any existing app from SAP Cloud Assistant for iOS Assistant. I just install and run it on my (real) iPhone 8, then I try to log in and the app crashes with the same error as above.

    Also I get the following:

    2018-04-26 13:33:03.636388+0200 GettingStarted[1652:439949] Unknown class FUIOnboardingButton in Interface Builder file.

    2018-04-26 13:33:03.636674+0200 GettingStarted[1652:439949] Unknown class FUIOnboardingButton in Interface Builder file.

    2018-04-26 13:33:03.636808+0200 GettingStarted[1652:439949] Unknown class FUITextView in Interface Builder file.

    2018-04-26 13:33:03.647789+0200 GettingStarted[1652:439949] Unknown class FUITextView in Interface Builder file.

     

     

    I hope you can help me

     

    Thanks

    Kevin

    • I have not myself encountered that error.
      It might be worth reviewing the version of Xcode, the SAP Cloud Platform for iOS SDK version and the version of iOS running on your device.

      Regards,
      Dan van Leeuwen

      • Thanks a lot. I had downloaded the SDK some weeks ago and it seems I had an old version. With the current one I don’t have any “exc_bad_access” error. The “unknown class” errors still persist but don’t make any problems.