iOS SDK quick start
You can use identity verification to securely verify the identity of your customers using documents like their driver’s license or passport—such as before allowing them to open a new bank account online or pick up a rental car. This guide describes how to quickly integrate identity verification into your iOS application using our iOS SDK, including both the client-side and backend integration.
Note
The SDK is in a beta release. So give us your feedback, and stay tuned for improvements...
How it works
Here's an example of a basic integration flow. Transmit APIs are shown in pink along with the relevant integration step.
After initializing the SDK (Step 3), your app starts a verification flow by creating a session in the backend to establish a secure context (Step 6 and Step 7) and then starting the session (Step 8). The SDK executes the verification process with the user using the Transmit identity verification experience. Once all the required images are submitted, Transmit starts processing the verification while the SDK polls for its status. Once processing is completed, the SDK notifies the app (Step 4) so it can obtain the verification result (Step 9) and proceed accordingly (Step 10).
Step 1: Configure your app
To integrate with Transmit, you'll need to configure an application.
From the Applications page, create a new application or use an existing one. From the application settings:
- For Client type , select native
- For Redirect URI , enter your website URL. This is a mandatory field, but it isn't used for this flow.
- Obtain your client ID and secret for API calls, which are autogenerated upon app creation.
Step 2: Add SDK to project
Add the SDK to your Xcode project so your application can access all the identity verification functionality.
-
To use
Swift Package Manager
: install the SDK as a dependency in your
Package.swift
. -
To use
CocoaPods
: specify the SDK in your
Podfile
.
dependencies: [
.package(url: "https://github.com/TransmitSecurity/identityVerification-ios-sdk.git", .upToNextMajor(from: "1.0.0"))
]
pod 'IdentityVerification', '~> 1.0.1'
Step 3: Initialize the SDK
Configure the SDK using one of the snippets below, where CLIENT_ID
is your client ID (obtained in Step 1):
-
To use
UIKit
: add the code to your
AppDelegate
or yourSceneDelegate
. - To use SwiftUI : add the code to your main app file.
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
TSIdentityVerification.initialize(clientId: [CLIENT_ID])
TSIdentityVerification.delegate = self
return true
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
TSIdentityVerification.initialize(clientId: [CLIENT_ID])
TSIdentityVerification.delegate = self
}
struct ExampleApp: App {
private let idvObserver = IDVStatusObserver()
init() {
TSIdentityVerification.initialize(clientId: [CLIENT_ID])
TSIdentityVerification.delegate = idvObserver
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
prvate class IDVObserver: TSIdentityVerificationDelegate {
func verificationDidReceiveStatus(_ status: IdentityVerification.TSIdentityVerificationStatus) {
...
}
func verificationDidFail(with error: IdentityVerification.TSIdentityVerificationError) {
...
}
}
Note
-
Make sure to add
import IdentityVerification
at the top of the implementation class. -
The SDK can be configured to work with an EU cluster by setting the first initialization parameter to
baseUrl : 'https://api.eu.transmitsecurity.io/verify'
.
Step 4: Observe status updates
Once a verification process has been initiated (as described in Step 8), the verification moves through different statuses. For example, the status will indicate if the process was completed successfully so the app can fetch the verification result. To observe status changes, add the extension shown below to the main class of your app.
Here are examples of adding the extension to your AppDelegate
or SceneDelegate
class:
extension AppDelegate: TSIdentityVerificationDelegate {
func verificationDidReceiveStatus(_ status: TSIdentityVerificationStatus) {
switch status {
case .pending:
// User has initiated the verification process
break
case .capturing:
// User has started to capture images
break
case .processing:
// User has finished uploading images and the verification is being processed
break
case .recapture:
// Verification process couldn't be completed, but the user can try again (Step 11)
break
case .completed:
// Verification process completed, and the result can be obtained (Step 9)
break
@unknown default:
break
}
}
func verificationDidFail(with error: IdentityVerification.TSIdentityVerificationError) {
print("Verification error: \(error.rawValue)")
}
}
extension SceneDelegate: TSIdentityVerificationDelegate {
func verificationDidReceiveStatus(_ status: TSIdentityVerificationStatus) {
switch status {
case .pending:
// User has initiated the verification process
break
case .capturing:
// User has started to capture images
break
case .processing:
// User has finished uploading images and the verification is being processed
break
case .recapture:
// Verification process couldn't be completed, but the user can try again (Step 11)
break
case .completed:
// Verification process completed, and the result can be obtained (Step 9)
break
@unknown default:
break
}
}
func verificationDidFail(with error: IdentityVerification.TSIdentityVerificationError) {
print("Verification error: \(error.rawValue)")
}
}
Note
Make sure to add import IdentityVerification
at the top of the implementation class.
Step 5: Add camera permission
Your app requires camera permissions in order to capture the images required for the verification process.
-
Open the
Info.plist
file as a Property List and add the following key: Privacy - Camera Usage Description . The key value contains an explanation for why the permission is needed, which will be displayed to the user to approve. For example: This is needed to capture images for the verification process . - To handle camera permissions, your client app should implement code like the snippet below.
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized: // User has already authorized camera access
// Start Identity Verification here
break
case .notDetermined: // User hasn't yet authorized camera access
AVCaptureDevice.requestAccess(for: .video) { (granted) in
if granted { // User has granted camera access
// Start Identity Verification here
} else {
// Handle unauthorized state
}
}
break
case .denied:
// Handle authorization state
break
case .restricted:
// Handle authorization state
break
@unknown default:
// Handle unauthorized state
break
}
Step 6: Get access tokens
An access token is required to authorize the backend API calls, such as for creating a verification session (Step 7) and obtaining the result (Step 9).
When needed, obtain an access token using the /token
request below.
import fetch from 'node-fetch';
async function run() {
const formData = {
client_id: '[CLIENT_ID]', // Replace with client ID obtained in Step 1
client_secret: '[CLIENT_SECRET]', // Replace with client secret obtained in Step 1
grant_type: 'client_credentials',
resource: 'https://verify.identity.security'
};
const resp = await fetch(
`https://api.transmitsecurity.io/oidc/token`, // Use api.eu.transmitsecurity.io for EU clusters
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams(formData).toString()
}
);
const data = await resp.text();
console.log(data);
}
run();
Notes
-
The token must be requested for the
https://verify.identity.security
resource, which will appear in the audience claim of the generated token. - The token must remain secure on your server, and must only be used for backend requests.
Step 7: Create session
Before your mobile app can initiate the verification process, your backend must create a session in order to provide a secure context for the flow. Create a session by sending the /verification
request below.
import fetch from 'node-fetch';
async function run() {
const resp = await fetch(
`https://api.transmitsecurity.io/verify/api/v1/verification`, // Use api.eu.transmitsecurity.io for EU clusters
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer [ACCESS_TOKEN]' // Replace with access token obtained in Step 6
}
}
);
const data = await resp.json();
console.log(data);
}
run();
The response contains a start_token
that will be used to start the verification on the client side (in Step 8), and the session_id
required to obtain the verification result (in Step 9). For example:
{
"start_token": "ca766ed78c8c0b7824dfea356ed30b72",
"session_id": "H1I12oskjzsdhskj4",
"expiration": "2023-07-18T09:57:46.950Z",
"missing_images": [
"document_front",
"document_back",
"selfie"
]
}
Step 8: Start session
Once a session is created, initiate the verification process using the start()
SDK method. Add the code below to your mobile app, passing the start_token
value returned in the previous step. If successful, the SDK will start a verification process for the user and guide them through the entire identity verification flow using the Transmit experience.
TSIdentityVerification.start(startToken: [START_TOKEN])
Note
Make sure to add import IdentityVerification
at the top of the implementation class.
Step 9: Get verification result
Once the verification process starts, your mobile app can track its status using the extension added in Step 4.
After all the required images are successfully submitted, Transmit automatically starts processing the verification and the SDK starts polling to check the status. If the status is completed
, your backend should send the request below to obtain the verification result (see API reference):
import fetch from 'node-fetch';
async function run() {
const sid = '[SESSION_ID]'; // Replace with session ID returned in Step 7
const resp = await fetch(
`https://api.transmitsecurity.io/verify/api/v1/verification/${sid}/result`, // Use api.eu.transmitsecurity.io for EU clusters
{
method: 'GET',
headers: {
Authorization: 'Bearer [ACCESS_TOKEN]' // Replace with access token obtained in Step 6
}
}
);
const data = await resp.text();
console.log(data);
}
run();
Note
See Step 11 for how to handle the recapture
status, in case it's returned.
Step 10: Handle verification result
Your app should define the user experience based on the verification result, indicated by the recommendation
field:
-
If
ALLOW
: the process was completed successfully. The response includes some user details collected from the document (like their name and birthday) which can be used to enrich the user's profile, and details about the document used to prove their identity. Those details will be under theverified_info
field of the response.
-
If
CHALLENGE
: the process didn’t succeed since at least one verification check didn’t pass. You should proceed as suitable for your use case, typically by initiating a manual review process. The extracted yet unverified info is available under theextracted_info
field of the response. This info can be used to review unsuccessful sessions or to search failed attempts.
-
If
DENY
: the process indicates a high likelihood of attempted fraud. You should block the user or initiate an in-depth manual review to avoid onboarding a fraudulent user. The extracted yet unverified info is available under theextracted_info
field of the response.
Here's an example of a successful verification result:
{
"session_id": "H1I12oskjzsdhskj4",
"status": "complete",
"recommendation": "ALLOW",
"extracted_info": {
/* same structure as verified_info; all fields are optional */
},
"verified_info": {
"person": {
"given_name": "Marie",
"surname": "Curie",
"gender": "F",
"full_name": "Marie Salomea Skłodowska-Curies",
"age": 36,
"address": {
"country": "USA",
"region": "Indiana",
"city": "Indianapolis",
"street": "Snowy Ridge Road",
"house_number": "1234",
"apartment_number": "12",
"postcode": "56789",
"full_address": "1234 Snowy Ridge Road Indianapolis, IN 56789"
},
"date_of_birth": "1867-11-07T00:00:00.000Z"
},
"document": {
"type": "national_id",
"country": "USA",
"region": "ny",
"issue_date": "1867-11-07T00:00:00.000Z",
"document_serial_number": "1234567",
"id_value": "123ABC",
"given_name": "Marie",
"surname": "Curie",
"full_name": "Marie Salomea Skłodowska-Curie",
"date_of_birth": "1867-11-07T00:00:00.000Z",
"valid_until": "1867-11-07T00:00:00.000Z"
}
}
}
Step 11: Recapture images
The recapture
status is returned in certain cases, such as if some data couldn't be extracted due to poor image quality. When this happens, your app can allow the user to try again by calling the recapture()
SDK call, which will recapture only the required images. Once the SDK submits the required images, the processing
status will be returned until the processing is completed. The app can then fetch the verification result as described in Step 9.
TSIdentityVerification.recapture()