The iOS Button SDK will provide your application with the ability to request attributes from a Digital ID app* via a simple "sign in with" style button. This repo contains the tools and step by step instructions so that your users can share their identity details with your application in a secure and trusted way.
Digital ID app is either the Yoti or EasyID iOS app
- Digital ID application should be installed on the device
- The minimum version of the deployment target should be iOS 12.0 or above.
- You will need Xcode 12.0 or above
There are three sections to complete installing the iOS Button SDK:
-
First please follow our Yoti dashboard process. You will need to create an organisation here. After organisation creation, you will need to create a Yoti application. If you are testing or using Yoti for personal use please go straight to creating an application here.
The application process will generate keys for you. Please keep your sdk id and scenario id safe for the mobile integration.
For more information please follow our developer page instructions located here.
-
Installing the web sdk. Please browse through our GitHub page and initialise the web sdk in your web backend.
For more information please follow our developer page instructions located here.
-
Installing the iOS Button SDK. This can be done using one of the methods below:
- Swift Package Manager - (recommended)
- Carthage
- Cocoapods
- Drag and drop
SPM is a decentralized dependency manager deeply integrated into Xcode and maintained by the Swift community project. This builds your dependencies and automatically links and embeds them in your application.
To integrate the iOS Button SDK into your Xcode project using SPM, simply add a new dependency in your Project's Swift Packages
Use the URL given by this repository's "Code" clone button.
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
To integrate iOS Button SDK into your Xcode project using Carthage, specify it in your Cartfile
:
github "getyoti/ios-sdk-button" ~> 4.0.0
This will allow you to type carthage update ios-sdk-button --use-xcframeworks
in your Terminal to fetch and build the latest version of the framework.
The first time you are integrating the iOS Button SDK, the dynamic framework will be generated at Carthage/Build/iOS/
.
Drag the built YotiButtonSDK.xcframework
into your Target's "Frameworks, Libraries, and Embedded Content". Don't commit the Carthage
folder but do commit the Carthage.resolved
file.
Each time you want to fetch the dependency, you can type carthage bootstrap --use-xcframeworks
.
CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. Installation instructions are in the guides.
To integrate Yoti into your Xcode project using CocoaPods, specify it in your Podfile
:
pod 'yoti-sdk', '~> 4.0.0'
Tip: CocoaPods provides a pod init
command to create a Podfile with smart defaults. You should use it.
Now you can install the dependencies in your project:
$ pod install
Make sure to always open the Xcode workspace instead of the project file when building your project.
You can also add iOS Button SDK by adding the project via a submodule and dragging the YotiButtonSDK project file into yours.
Class: YotiButton
Module: YotiButtonSDK
Navigate to where you want to integrate the button, add a UIView and change the class to YotiButton.
Add layout constraints and then add a User defined runtime attributes: useCaseID
of type String with a value that will allow us to identify the button.
Link the button view with an appropriate IBOutlet.
Programatically assign a closure/block to the action property.
This action property returns the button instance to allow the same closure/block to be reused across multiple buttons.
You can simply instantiate the button providing a frame and assigning a useCaseID
as shown in the following examples:
import YotiButtonSDK
let button = YotiButton(frame: YotiButton.defaultFrame)
button.useCaseID = "YOUR_USE_CASE_ID"
button.action = { [weak self] (button) in
self?.handleButtonTap(button)
}
If you integrated using Cocoapods please use the following:
import yoti_sdk
let button = YotiButton(frame: CGRect(x: 0, y: 0, width: 300, height: 44))
button.useCaseID = "YOUR_USE_CASE_ID"
button.action = { [weak self] (button) in
self?.handleButtonTap(button)
}
#import <YotiButtonSDK/YotiButtonSDK.h>
YotiButton* button = [[YotiButton alloc] initWithFrame:CGRectMake(0, 0, 300, 44)]
button.useCaseID = @"YOUR_USE_CASE_ID"
MyViewController * __weak weakSelf = self;
self.button.action = ^void(YotiButton* button) {
[weakSelf handleButtonTap:button];
};
The iOS Button SDK supports a few themes which have slightly different behaviours:
Name | Colour | Target App |
---|---|---|
yoti | Blue | Digital ID Apps |
yotiUK | Blue with white logo | Digital ID Apps |
easyID | Red with white logo | EasyID App |
partnership | White with supplementary view | Digital ID Apps |
Depending on which theme you select it may target a specific app or have a supplementary view underneath it. The way each themed button is used however remains the same.
To set the theme simply alter theme
property on your button with a new enum case.
By default, the partnership
theme will be selected at runtime for users in the United Kingdom and yoti
for everyone else.
button.theme = .easyID
button.theme = YTBThemeEasyID
The UI integration is now complete.
You will now need your SDK ID, Scenario ID and call back URL ready from your application dashboard.
For each of the scenarios you want to handle, you would need to add them to the iOS Button SDK like below:
Please add the scenario method in your AppDelegate.swift or somewhere before the button is accessible at runtime (ensuring it is only called once):
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
like below.
import YotiButtonSDK
do {
guard let url = URL(string: "YOUR_CALLBACK_URL") else {
return false
}
//Here we need to add as many scenarios as we want. each scenario is linked to a button in the Main.storyboard.
let firstScenario = try ScenarioBuilder().setUseCaseID("YOUR_FIRST_USE_CASE_ID")
.setClientSDKID("YOUR_FIRST_CLIENT_SDK_ID")
.setScenarioID("YOUR_FIRST_SCENARIO_ID_1")
.setCallbackBackendURL(url)
.create()
YotiSDK.add(scenario: firstScenario)
let secondScenario = try ScenarioBuilder().setUseCaseID("YOUR_SECOND_USE_CASE_ID")
.setClientSDKID("YOUR_SECOND_CLIENT_SDK_ID")
.setScenarioID("YOUR_SECOND_SCENARIO_ID_2")
.setCallbackBackendURL(url)
.create()
YotiSDK.add(scenario: secondScenario)
} catch {
// handle error code here
}
}
Please add the scenario method in your AppDelegate.m or somewhere before the button is accessible at runtime (ensuring it is only called once):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
like below
#import <YotiButtonSDK/YotiButtonSDK.h>
NSError* error = nil;
YTBScenarioBuilder *firstScenarioBuilder = [[YTBScenarioBuilder alloc] init];
firstScenarioBuilder.useCaseID = @"YOUR_FIRST_USE_CASE_ID";
firstScenarioBuilder.clientSDKID = @"YOUR_FIRST_CLIENT_SDK_ID";
firstScenarioBuilder.scenarioID = @"YOUR_FIRST_SCENARIO_ID";
firstScenarioBuilder.callbackBackendURL = [NSURL URLWithString:@"YOUR_CALLBACK_URL"];
YTBScenario *firstScenario = [firstScenarioBuilder create:&error];
[YotiSDK addScenario: firstScenario];
error = nil;
YTBScenarioBuilder *secondScenarioBuilder = [[YTBScenarioBuilder alloc] init];
secondScenarioBuilder.useCaseID = @"YOUR_SECOND_USE_CASE_ID";
secondScenarioBuilder.clientSDKID = @"YOUR_SECOND_CLIENT_SDK_ID";
secondScenarioBuilder.scenarioID = @"YOUR_SECOND_SCENARIO_ID";
secondScenarioBuilder.callbackBackendURL = [NSURL URLWithString:@"YOUR_CALLBACK_URL"];
YTBScenario *secondScenario = [secondScenarioBuilder create:&error];
[YotiSDK addScenario: secondScenario];
return YES;
Next, go to the view controller containing the YotiButton. In the handler function you embedded inside the button action body, call the iOS Button SDK startScenario
method.
func handleButtonTap(_ sender: YotiButton) {
guard let useCaseID = sender.useCaseID else {
return
}
do {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
try YotiSDK.startScenario(for: useCaseID, with: self)
} catch {
// Handle error here
}
}
- (void)handleButtonTap:(YotiButton*)sender {
NSString* useCaseID = sender.useCaseID;
NSError* error = nil;
if (![useCaseID isEqual:@""]) {
[YotiSDK startScenarioForUseCaseID:useCaseID withDelegate:self error:&error];
if (error != nil) {
NSLog(@"error : %@", error.description);
}
}
}
In Swift your ViewController class should comply to YotiSDKDelegate and to BackendDelegate in order to get callbacks from the iOS Button SDK.
extension ViewController: YotiSDKDelegate {
func yotiSDKDidFail(for useCaseID: String, appStoreURL: URL?, with error: Error) {
// Handle the errors related to the failure of retrieving a useCaseID and a token or opening the Digital ID app on the user's device
// Note the error will contain an associated App Store URL where the user can download the application in case the iOS Button SDK was not able to open the Digital ID app. The appStoreURL parameter is for Objective-C compatibility and is redundant.
}
func yotiSDKDidSucceed(for useCaseID: String, baseURL: URL?, token: String?, url: URL?) {
// Handle here the success of the opening of Yoti app for example by requesting a profile from the backend like below
// Get the specific scenario by calling
let scenario = YotiSDK.scenario(for: useCaseID)
// Request the backend to get the profile linked to a specific scenario by passing the token returned and self as delegate for a callback
YotiSDK.callbackBackend(scenario: scenario!, token: token!, with: self)
}
func yotiSDKDidOpenYotiApp() {
// Handle specific behaviour if needed when the Yoti App didOpen
}
}
In Objective-C, your viewController should conform to YTBSDKDelegate and YTBBackendDelegate like this:
@interface ViewController () <YTBSDKDelegate, YTBBackendDelegate>
BackendDelegate is its name in swift, YTBBackendDelegate namespace is for Objective-C Note that conforming to YTBBackendDelegate is optional because you might or not rely on a backend to implement your scenarios. Our current implementation relies on a backend to implement our scenarios.
We implemented the delegate functions of the protocols our ViewController complies to like this:
- (void)yotiSDKDidFailFor:(NSString * _Nonnull)useCaseID appStoreURL:(NSURL * _Nullable)appStoreURL with:(NSError * _Nonnull)error {
// Handle the errors related to the failure of retrieving a useCaseID and a token or opening the Digital ID app on the user's device
}
- (void)yotiSDKDidSucceedFor:(NSString * _Nonnull)useCaseID baseURL:(NSURL * _Nullable)baseURL token:(NSString * _Nullable)token url:(NSURL * _Nullable)url {
YTBScenario *scenario = [YotiSDK scenarioforUseCaseID:useCaseID];
[YotiSDK callbackBackendScenario:scenario token:token withDelegate:self];
}
- (void)yotiSDKDidOpenYotiApp {
// Behaviour when SDK opens yoti app (if needed)
}
when the callback returns from the backend we get the data linked to the profile or the error in
func backendDidFinish(with data: Data?, error: Error?)
- (void)backendDidFinishWith:(NSData * _Nullable)data error:(NSError * _Nullable)error
In case you need to change your interface before we are doing a network request, you can subscribe to the willMakeNetworkRequest
and didFinishNetworkRequest
notification from the YotiSDK
class
NotificationCenter.default.addObserver(forName: YotiSDK.willMakeNetworkRequest, object: nil, queue: nil) { (notification) in
// Disable interface
}
NotificationCenter.default.addObserver(forName: YotiSDK.didFinishNetworkRequest, object: nil, queue: nil) { (notification) in
// Re-enable interface
}
[NSNotificationCenter.defaultCenter addObserverForName:YotiSDK.willMakeNetworkRequest object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
// Disable interface
}];
[NSNotificationCenter.defaultCenter addObserverForName:YotiSDK.didFinishNetworkRequest object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
// Re-enable interface
}];
iOS Button SDK will perform an app switch to the appropriate app and back to your app to complete the sharing process, your application's .plist
also need to handle this.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>YOUR_URL_SCHEME</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>yoti</string>
<string>easyid</string>
</array>
import YotiButtonSDK
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ app: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return YotiSDK.application(app, open:url, options:options)
}
}
#import <YotiButtonSDK/YotiButtonSDK.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
return [YotiSDK application:app open:url options:options];
}
@end
The Web SDK will handle the user storage. When you retrieve the user profile, you receive a user ID generated by Yoti exclusively for your application. This means that if the same individual logs into another app, Yoti will assign her/him a different ID. You can use this ID to verify whether (for your application) the retrieved profile identifies a new or an existing user. Please see https://developers.yoti.com/ for more information.
For any questions or support please email [email protected]. Please provide the following to get you up and working as quickly as possible:
- iOS version of the test device
- Language of Web SDK
- Screenshot of error
Once we have answered your question we may contact you again to discuss Yoti products and services. If you’d prefer us not to do this, please let us know when you e-mail.