# iOS SDK

# Getting started

Latest release: Version 1.14.0 (Changelog)

# Requirements

  • iOS 9.0 or later (FaceScan3D module requires iOS 10+)
  • Xcode 11+

# Resources

# Installation

This framework is meant to be installed via cocoapods.

# Podfile

Update your Podfile:

  • Add source options for SumSubstance and Cocoapods repositories
  • Add IdensicMobileSDK dependency to your target
platform :ios, '9.0'

source 'https://cdn.cocoapods.org/'
source 'https://github.com/SumSubstance/Specs.git'

target 'YourApp' do

  pod 'IdensicMobileSDK'

  # any other dependencies
end

Then run this in your project directory

pod install

# Permissions

The framework will ask for access to the camera and possibly the microphone and the photo library too. Because of this, it is required to have the corresponding usage descriptions in the application's Info.plist file:

<key>NSCameraUsageDescription</key>
<string>Let us take a photo</string>
<key>NSMicrophoneUsageDescription</key>
<string>Time to record a video</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Let us pick a photo</string>

Also, if your app targets iOS 10 and below, it's required to turn on the iCloud Documents capabilities in Xcode as described here

# Basic Usage

Make sure you did the Backend routines before initializing the SDK

# Initialization

First of all, import the framework:

import IdensicMobileSDK

Then declare the initialization parameters:

let baseUrl = "https://test-api.sumsub.com" // or "https://api.sumsub.com" for production
let flowName = "msdk-basic-kyc" // the name of the applicant flow (must be set up via the dashboard)
let accessToken = "..." // use the `accessToken` for the applicant to be verified got from your backend
let locale = Locale.current.identifier // locale in the form of "en" or "en_US"
let supportEmail = "[email protected]" // optional support email

While baseUrl, flowName and accessToken are mandatory, locale and supportEmail are optional

  • If you do not provide locale, the current one will be used automatically
  • Setting supportEmail is just a convenient way to configure the Support screen; it's ok to set it to nil and configure Support Items later on.

Next you instantiate SNSMobileSDK and check if setup succeeded:

let sdk = SNSMobileSDK(
    baseUrl: baseUrl,
    flowName: flowName,
    accessToken: accessToken,
    locale: locale,
    supportEmail: supportEmail
)

guard sdk.isReady else {
    print("Initialization failed: " + sdk.verboseStatus)
    return
}

Here you create an instance of the SDK and ensure that the setup was successful with sdk.isReady, if it happens to fail, you will be able to pinpoint the reason by printing sdk.verboseStatus.

Most likely, you will then tune up the sdk a bit further by assigning Handlers and Callbacks, and possibly by applying some Customization, but that's just an optional feature.

We know that getting an access token forces you to ask your backend and due to the async nature of this process you'll have to build some UI around. For your convenience, it's possible to postpone the provision of the token until the sdk has been presented. To do so, you will need to pass an empty string as the accessToken at the initialization stage and set the tokenExpirationHandler (see below) that would be called immediately after the sdk is appeared up at the time when it shows an activity indicator.

# Presentation

In any case once setup is done you are ready to present the SDK on the screen:

yourViewController.present(sdk.mainVC, animated: true, completion: nil)

Since SDK contains its own navigation stack, it's required to be presented modally instead of being pushed.

You can also use a shortcut like this:

sdk.present(from: yourViewController)

Or even shorter if it's comfortable for you to present the sdk on the key window's root view controller:

sdk.present()

# Dismission

Normally the user closes the sdk himself, but if you need to do it programmatically, here is the helper:

sdk.dismiss()

# Handlers

Technically the handlers are optional, but you are strongly encouraged to provide at least tokenExpirationHandler to make sure that the access token always is live.

# Token Expiration

Because of the limited lifespan of the accessToken, it's important that you are able to handle the situation where the token expires and needs to be refreshed. As a solution to this, you set tokenExpirationHandler. The handler should make a call to your backend, obtain the newToken and then pass it back to the sdk by running onComplete closure.

sdk.tokenExpirationHandler { (onComplete) in
    get_token_from_your_backend { (newToken) in
        onComplete(newToken)
    }
}

⚠️ onComplete must be executed even if you fail to provide a new token, just pass nil in this case.

# Verification Completion

You can use verificationHandler to be informed when the verification process has been concluded with a final decision. The parameter isApproved lets you know if the applicant was approved or finally rejected. If you'd like to get notified about any other stages of the verification process, use onStatusDidChange described below.

sdk.verificationHandler { (isApproved) in
    print("verificationHandler: Applicant is " + (isApproved ? "approved" : "finally rejected"))
}

# Dismission Control

You can take over the dismissal control by assigning dismissHandler. The handler takes the current sdk instance and the mainVC controller. It's up to you to dismiss the mainVC in the manner that you see fit.

sdk.dismissHandler { (sdk, mainVC) in
    mainVC.dismiss(animated: true, completion: nil)
}

# Callbacks

Callbacks are completely optional. Use them if you feel that they will be helpful.

# Status Updates Notification

Use onStatusDidChange callback to get notified about the stages of the verification process. The callback takes two parameters. The first one sdk is the SDK instance, and the last one prevStatus gives you a chance to know the previous value of the status. Following this, you are able to examine sdk.status enum in order to determine the current sdk status.

sdk.onStatusDidChange { (sdk, prevStatus) in

    print("onStatusDidChange: [\(sdk.description(for: prevStatus))] -> [\(sdk.description(for: sdk.status))]")

    switch sdk.status {

    case .ready:
        // Technically .ready couldn't ever be passed here, since the callback has been set after `status` became .ready
        break

    case .failed:
        print("failReason: [\(sdk.description(for: sdk.failReason))] - \(sdk.verboseStatus)")

    case .initial:
        print("No verification steps are passed yet")

    case .incomplete:
        print("Some but not all of the verification steps have been passed over")

    case .pending:
        print("Verification is pending")

    case .temporarilyDeclined:
        print("Applicant has been temporarily declined")

    case .finallyRejected:
        print("Applicant has been finally rejected")

    case .approved:
        print("Applicant has been approved")

    case .actionCompleted:
        print("Applicant action has been completed")
    }
}

# Dismiss Notification

An optional way to be notified when mainVC is dismissed:

sdk.onDidDismiss { (sdk) in
    print("onDidDismiss: sdk has been dismissed with status [\(sdk.description(for: sdk.status))]")
}

# Logging

There's no need to mention the importance of logs in the case that something goes wrong.

# Log Level

By default SDK tries not to spam your console and will print only when something critical has happened, however, sometimes, it makes sense to know what's going on under the hood.

You can choose the desired logLevel from .off that logs nothing, through .error (default), .warning, .info, .debug and up to .trace that will try to log as much as possible.

sdk.logLevel = .error

# Log Interception

By default SDK uses NSLog for logging purposes. If, for some reason, it does not work for you, feel free to use logHandler to intercept log messages and direct them as required.

sdk.logHandler { (level, message) in
    print(Date(), "[Idensic] \(message)")
}

# Customization

# Support Items

Support items define the ways in which your users will be prompted to contact you at the Support screen. Initially if non-empty supportEmail parameter passed during initialization, then an Email item would be created automatically.

Feel free to reconfigure support items as necessary. In order to do so, you could either assign an array of items to sdk.supportItems property directly, or use sdk.addSupportItem helper to add them one by one.

sdk.addSupportItem { (item) in
    item.title = NSLocalizedString("URL Item", comment: "")
    item.subtitle = NSLocalizedString("Tap me to open an url", comment: "")
    item.icon = UIImage(named: "AppIcon")
    item.actionURL = URL(string: "https://google.com")
}

sdk.addSupportItem { (item) in
    item.title = NSLocalizedString("Callback Item", comment: "")
    item.subtitle = NSLocalizedString("Tap me to get callback fired", comment: "")
    item.icon = UIImage(named: "AppIcon")
    item.actionHandler { (supportVC, item) in
        print("[\(item.title)] tapped")
    }
}

Each item must have a mandatory title, optional icon, subtitle and an action expressed with actionURL or actionHandler. If actionHandler is defined, it would be called when the item is tapped, otherwise actionURL would be opened with UIApplication's openURL: method. If no actionURL and no actionHandler are provided, then no action would be taken on tap.

# Theme

The theme allows you to customize things such as fonts, colors and images used across SDK. The default theme is accessible once the sdk is initialized. So depend on your needs you either adjust the theme in place

sdk.theme.sns_CameraScreenTorchButtonTintColor = .white

or inherit from SNSTheme and apply your own theme at once

sdk.theme = OwnTheme()

class OwnTheme: SNSTheme {
    override init() {
        super.init()

        sns_CameraScreenTorchButtonTintColor = .white
    }
}

All of the customizable options start from sns_ prefix, so when you start typing, you will begin to see them all.

# Localization

You can customize or localize the texts used within the SDK through the MSDK Translations tool in the dashboard.

# Applicant Actions

There is a special way to use the SDK in order to perform Applicant actions.

Only the Face authentication action is supported at the moment

# Action Flow

In order to run the SDK in applicant action mode, you need to create an applicant flow of Applicant actions type in the dashboard and specify its name as the flowName initialization parameter. Also, it'll be required to make an Access Token not only with the userId parameter, but with the externalActionId one as well.

Aside from the notes above, you manage the sdk the same way that you do with regular flows, the only difference is in how you get the action's result.

When an action is completed, the sdk.status will be set to .actionCompleted and sdk.actionResult will contain the outcome of the last action's invocation.

You can use onDidDismiss, dismissHandler or onStatusDidChange callback in order to determine the sdk's status and get the action's result.

For example:

sdk.onDidDismiss { (sdk) in
    
    switch sdk.status {
        
    case .failed:
        print("failReason: [\(sdk.description(for: sdk.failReason))] - \(sdk.verboseStatus)")
    
    case .actionCompleted:
        // the action was performed or cancelled
        
        if let result = sdk.actionResult {
            print("Last action result: actionId=\(result.actionId) answer=\(result.answer ?? "<none>")")
        } else {
            print("The action was cancelled")
        }
        
    default:
        // in case of an action flow, the other statuses are not used for now,
        // but you could see them if the user closes the sdk before the flow is loaded
        break
    }
}

# Action Result

The action's result is represented by the sdk.actionResult property, that contains the following fields:

Field Type Description
actionId String Applicant action identifier to check the results against the server
answer String? Overall result. Typical values are GREEN, RED or ERROR

The absence of the sdk.actionResult means that the user has cancelled the process.

# Result Handler

In addition, there is an optional actionResultHandler, that allows you to handle the action's result upon it's arrival from the backend. The user sees the "Processing..." screen at this moment.

sdk.actionResultHandler { (sdk, result, onComplete) in

    print("actionResultHandler: actionId=\(result.actionId) answer=\(result.answer ?? "<none>")")
    
    // you are allowed to process the result asynchronously, just don't forget to call `onComplete` when you finish,
    // you could pass `.cancel` to force the user interface to close, or `.continue` to proceed as usual
    onComplete(.continue)
}

⚠️ onComplete must be executed at the end of processing.

Last Updated: 11/18/2020, 4:25:56 PM