Skip to Content

Getting Started with the SAP Cloud Platform SDK for iOS

Part 2 — Registration using SAPFioriFlows

Previous (Setup)   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, an 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 and 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.

      <?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. Add the following import to the other imports at the top of ViewController.
      import SAPFioriFlows
      
    4. 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(),
          ]
      }​
    5. 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)")!
      }​
    6. At the end of viewDidLoad function, start the onboarding process.
      getAppConfig()
      UINavigationBar.applyFioriStyle()
      onboardOrRestore()
    7. 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
      
    8. 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.
    9. 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 domain of the email address must match one of the configured domains in the SAP Discovery Service.

    10. 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)   Next (Logging)

To report this post you need to login first.

13 Comments

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

  1. Vishesh Agrawal

    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?

    (1) 
    1. Daniel Van Leeuwen
      Post author

      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

      (0) 
      1. Vishesh Agrawal

        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

        (0) 
          1. Daniel Van Leeuwen
            Post author

            Based on the feedback from Andreas below, the specific steps to not show the Welcome screen for the example shown in this blog post would be to comment out

            self.configuredWelcomeScreenStep()

            and add the following lines near the top of the onboardOrRestore method.

            let settingsParameters = SAPcpmsSettingsParameters(backendURL: URL(string: "https://hcpms-XXXXXtrial.hanatrial.ondemand.com/")!, applicationID: "com.iossdk.gs")
            myContext.info[.sapcpmsSettingsParameters] = settingsParameters
            myContext.info[.authenticationURL] = URL(string: "https://hcpms-XXXXtrial.hanatrial.ondemand.com/com.sap.edm.sampleservice")!

            Regards,

            Dan van Leeuwen

            (0) 
    2. Andreas Schlosser

      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

      (0) 
  2. Christoph Lückler

    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

     

    (0) 
  3. Kevin Deggelmann

    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

    (0) 
    1. Daniel Van Leeuwen
      Post author

      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

      (0) 
      1. Kevin Deggelmann

        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.

        (0) 

Leave a Reply