diff --git a/README.md b/README.md
index 926e592..db5358f 100644
--- a/README.md
+++ b/README.md
@@ -18,20 +18,20 @@ react-native link react-native-facetec-zoom
### iOS
-This library has been tested with version 8.1.0 of the SDK
+This library has been tested with version 9.0.1 of the iOS SDK (Android support pending)
-First, download `ZoomAuthentication.framework` from one of these sources:
+First, download `FaceTecSDK.framework` from one of these sources:
-- [Zoom SDK](https://dev.zoomlogin.com/zoomsdk/#/downloads)
-- [app.tradle.io](https://s3.amazonaws.com/app.tradle.io/sdk/facetec-zoom/ios/8.1.0/ZoomAuthentication.framework.zip)
+- [Zoom SDK](https://dev.facetec.com/downloads)
-Unzip the file, locate `ZoomAuthentication.framework`, copy it to your `ios/` directory, and drag it to your project in XCode (Check the `Copy items if needed` option when asked)
+Unzip the file, locate `FaceTecSDK.framework`, copy it to your `ios/` directory, and drag it to your project in XCode (Check the `Copy items if needed` option when asked)
-Add a Copy File phase to your Xcode project and have `ZoomAuthentication.framework` copied to Destination `Frameworks`
+Add a Copy File phase to your Xcode project and have `FaceTecSDK.framework` copied to Destination `Frameworks`
If you have an Objective-C project, add a blank Swift file to your project (File -> New -> Swift File), with a bridging header (it will prompt you to auto-create one).
add `NSCameraUsageDescription` to your Info.plist, e.g.:
+
```xml
NSCameraUsageDescription
verify liveness with Zoom
@@ -41,7 +41,7 @@ add `NSCameraUsageDescription` to your Info.plist, e.g.:
In your project's build.gradle (android/build.gradle), add the maven block below:
-```gradle
+````gradle
allprojects {
repositories {
// ...
@@ -58,7 +58,7 @@ ext {
zoomSdkVersion = '7.0.11' // <--- whichever version you want to use
// ...
}
-```
+````
```
@@ -67,3 +67,4 @@ This module depends on [react-native-image-store](https://github.com/tradle/reac
## Usage
See example app at https://github.com/tradle/rnzoomexample
+```
diff --git a/index.js b/index.js
index 0b04f16..e6f1cb0 100644
--- a/index.js
+++ b/index.js
@@ -11,7 +11,7 @@ export const status = {
const wrapNative = native => {
let initialized
- let verifying
+ let processing
const initialize = async opts => {
initialized = false
@@ -28,33 +28,48 @@ const wrapNative = native => {
}
const getVersion = () => native.getVersion()
- const verify = async (opts = {}) => {
+ const exec = async (operation, opts = {}) => {
if (!initialized) {
throw new NotInitializedError('initialize me first!')
}
- if (verifying) {
+ if (processing) {
throw new VerificationPendingError('only one verification can be done at a time')
}
let result
try {
- result = await native.verify({
- ...defaults.verify,
+ result = await native[operation]({
+ ...defaults[operation],
...opts,
})
result.status = statusToString(result.status)
return result
} finally {
- verifying = false
+ processing = false
}
}
+ const enroll = (opts) => {
+ if (typeof opts.id !== 'string' || !opts.id) throw new Error('expected string "id"')
+
+ return exec('enroll', opts)
+ }
+
+ const authenticate = (opts) => {
+ if (typeof opts.id !== 'string' || !opts.id) throw new Error('expected string "id"')
+
+ return exec('authenticate', opts)
+ }
+
return {
- preload: native.preload,
initialize,
- verify,
+ verifyLiveness: exec.bind(null, 'verifyLiveness'),
+ // backwards compat
+ verify: exec.bind(null, 'verifyLiveness'),
+ enroll,
+ authenticate,
getVersion,
}
}
diff --git a/ios/AuthenticateProcessor.swift b/ios/AuthenticateProcessor.swift
new file mode 100644
index 0000000..35c0621
--- /dev/null
+++ b/ios/AuthenticateProcessor.swift
@@ -0,0 +1,177 @@
+//
+// Welcome to the annotated FaceTec Device SDK core code for performing Authentication against a previously enrolled user.
+//
+
+import UIKit
+import Foundation
+import FaceTecSDK
+
+// This is an example self-contained class to perform Authentication with the FaceTec SDK.
+// You may choose to further componentize parts of this in your own Apps based on your specific requirements.
+class AuthenticateProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSessionTaskDelegate {
+ var latestNetworkRequest: URLSessionTask!
+ var isSuccess = false
+ var faceScanResultCallback: FaceTecFaceScanResultCallback!
+ var licenseKey: String!
+ var delegate: ProcessingDelegate
+ var latestZoomSessionResult: FaceTecSessionResult?
+ var options: Dictionary!
+
+ init(sessionToken: String, delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) {
+ self.isSuccess = false;
+ self.delegate = delegate;
+
+ self.options = options;
+ self.licenseKey = options["licenseKey"] as? String
+
+ super.init()
+ //
+ // Part 1: Starting the FaceTec Session
+ //
+ // Required parameters:
+ // - delegate:
+ // - faceScanProcessorDelegate: A class that implements FaceTecFaceScanProcessor, which handles the FaceScan when the User completes a Session. In this example, "self" implements the class.
+ // - sessionToken: A valid Session Token you just created by calling your API to get a Session Token from the Server SDK.
+ //
+ let sessionVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken)
+ if (options["useOverlay"] as? Bool ?? false) {
+ sessionVC.modalPresentationStyle = .overCurrentContext
+ }
+
+ // In your code, you will be presenting from a UIViewController elsewhere. You may choose to augment this class to pass that UIViewController in.
+ // In our example code here, to keep the code in this class simple, we will just get the Sample App's UIViewController statically.
+ fromVC.present(sessionVC, animated: true, completion: nil)
+ }
+
+ //
+ // Part 2: Handling the Result of a FaceScan
+ //
+ func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) {
+ //
+ // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor.
+ // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like.
+ //
+// fromViewController.setLatestSessionResult(sessionResult: sessionResult)
+
+ self.latestZoomSessionResult = sessionResult
+
+ //
+ // DEVELOPER NOTE: A reference to the callback is stored as a class variable so that we can have access to it while performing the Upload and updating progress.
+ //
+ self.faceScanResultCallback = faceScanResultCallback
+
+ //
+ // Part 3: Handles early exit scenarios where there is no FaceScan to handle -- i.e. User Cancellation, Timeouts, etc.
+ //
+ if sessionResult.status != FaceTecSessionStatus.sessionCompletedSuccessfully {
+ if latestNetworkRequest != nil {
+ latestNetworkRequest.cancel()
+ }
+
+ faceScanResultCallback.onFaceScanResultCancel()
+ return
+ }
+
+ //
+ // Part 4: Get essential data off the FaceTecSessionResult
+ //
+ var parameters: [String : Any] = [:]
+ parameters["faceScan"] = sessionResult.faceScanBase64
+ parameters["auditTrailImage"] = sessionResult.auditTrailCompressedBase64![0]
+ parameters["lowQualityAuditTrailImage"] = sessionResult.lowQualityAuditTrailCompressedBase64![0]
+ parameters["externalDatabaseRefID"] = self.options["id"] as! String
+
+ //
+ // Part 5: Make the Networking Call to Your Servers. Below is just example code, you are free to customize based on how your own API works.
+ //
+ let request = NSMutableURLRequest(url: NSURL(string: ZoomGlobalState.ZoomServerBaseURL + "/match-3d-3d")! as URL)
+ request.httpMethod = "POST"
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions(rawValue: 0))
+ request.addValue(self.licenseKey, forHTTPHeaderField: "X-Device-Key")
+ request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(sessionResult.sessionId), forHTTPHeaderField: "User-Agent")
+
+ let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
+ latestNetworkRequest = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
+ //
+ // Part 6: In our Sample, we evaluate a boolean response and treat true as success, false as "User Needs to Retry",
+ // and handle all other non-nominal responses by cancelling out. You may have different paradigms in your own API and are free to customize based on these.
+ //
+ guard let data = data else {
+ // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error.
+ faceScanResultCallback.onFaceScanResultCancel()
+ return
+ }
+
+ guard let responseJSON = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject] else {
+ // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error.
+ faceScanResultCallback.onFaceScanResultCancel()
+ return
+ }
+
+ //
+ // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor.
+ // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like.
+ //
+// self.fromViewController.setLatestServerResult(responseJSON: responseJSON)
+
+ let didSucceed = responseJSON["success"] as? Bool
+
+ if didSucceed == true {
+ // CASE: Success! Authentication succeeded.
+
+ //
+ // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor.
+ // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like.
+ //
+ self.isSuccess = true;
+
+ FaceTecCustomization.setOverrideResultScreenSuccessMessage("Authenticated")
+ faceScanResultCallback.onFaceScanResultSucceed()
+ }
+ // this includes the case where there was no match, so no point in retrying
+// else if (didSucceed == false) {
+// // CASE: In our Sample code, "success" being present and false means that the User Needs to Retry.
+// // Real Users will likely succeed on subsequent attempts after following on-screen guidance.
+// // Attackers/Fraudsters will continue to get rejected.
+// faceScanResultCallback.onFaceScanResultRetry()
+// }
+ else {
+ // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error.
+ faceScanResultCallback.onFaceScanResultCancel()
+ }
+ })
+
+ //
+ // Part 8: Actually send the request.
+ //
+ latestNetworkRequest.resume()
+
+ //
+ // Part 9: For better UX, update the User if the upload is taking a while. You are free to customize and enhance this behavior to your liking.
+ //
+ DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
+ if self.latestNetworkRequest.state == .completed { return }
+
+ let uploadMessage:NSMutableAttributedString = NSMutableAttributedString.init(string: "Still Uploading...")
+ faceScanResultCallback.onFaceScanUploadMessageOverride(uploadMessageOverride: uploadMessage)
+ }
+ }
+
+ //
+ // URLSessionTaskDelegate function to get progress while performing the upload to update the UX.
+ //
+ func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
+ let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
+ faceScanResultCallback.onFaceScanUploadProgress(uploadedPercent: uploadProgress)
+ }
+
+ //
+ // Part 10: This function gets called after the FaceTec SDK is completely done. There are no parameters because you have already been passed all data in the processSessionWhileFaceTecSDKWaits function and have already handled all of your own results.
+ //
+ func onFaceTecSDKCompletelyDone() {
+ // In your code, you will handle what to do after Authentication is successful here.
+ // In our example code here, to keep the code in this class simple, we will call a static method on another class to update the Sample App UI.
+ delegate.onProcessingComplete(isSuccess: isSuccess, facetecSessionResult: latestZoomSessionResult)
+ }
+}
diff --git a/ios/EnrollmentProcessor.swift b/ios/EnrollmentProcessor.swift
new file mode 100644
index 0000000..be54a28
--- /dev/null
+++ b/ios/EnrollmentProcessor.swift
@@ -0,0 +1,167 @@
+//
+// Welcome to the annotated FaceTec Device SDK core code for performing secure Enrollments!
+//
+
+import UIKit
+import Foundation
+import FaceTecSDK
+
+// This is an example self-contained class to perform Enrollments with the FaceTec SDK.
+// You may choose to further componentize parts of this in your own Apps based on your specific requirements.
+class EnrollmentProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSessionTaskDelegate {
+ var latestNetworkRequest: URLSessionTask!
+ var licenseKey: String!
+ var delegate: ProcessingDelegate
+ var isSuccess: Bool
+ var latestZoomSessionResult: FaceTecSessionResult?
+ var faceScanResultCallback: FaceTecFaceScanResultCallback!
+ var options: Dictionary!
+
+ init(sessionToken: String, delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) {
+ self.isSuccess = false;
+ self.delegate = delegate;
+
+ self.options = options;
+ self.licenseKey = options["licenseKey"] as? String
+
+ super.init()
+ //
+ // Part 1: Starting the FaceTec Session
+ //
+ // Required parameters:
+ // - delegate:
+ // - faceScanProcessorDelegate: A class that implements FaceTecFaceScanProcessor, which handles the FaceScan when the User completes a Session. In this example, "self" implements the class.
+ // - sessionToken: A valid Session Token you just created by calling your API to get a Session Token from the Server SDK.
+ //
+ let sessionVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken)
+ if (options["useOverlay"] as? Bool ?? false) {
+ sessionVC.modalPresentationStyle = .overCurrentContext
+ }
+
+ // In your code, you will be presenting from a UIViewController elsewhere. You may choose to augment this class to pass that UIViewController in.
+ // In our example code here, to keep the code in this class simple, we will just get the Sample App's UIViewController statically.
+ fromVC.present(sessionVC, animated: true, completion: nil)
+ }
+
+ //
+ // Part 2: Handling the Result of a FaceScan
+ //
+ func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) {
+
+ self.latestZoomSessionResult = sessionResult
+
+ //
+ // DEVELOPER NOTE: A reference to the callback is stored as a class variable so that we can have access to it while performing the Upload and updating progress.
+ //
+ self.faceScanResultCallback = faceScanResultCallback
+
+ //
+ // Part 3: Handles early exit scenarios where there is no FaceScan to handle -- i.e. User Cancellation, Timeouts, etc.
+ //
+ if sessionResult.status != FaceTecSessionStatus.sessionCompletedSuccessfully {
+ if latestNetworkRequest != nil {
+ latestNetworkRequest.cancel()
+ }
+
+ faceScanResultCallback.onFaceScanResultCancel()
+ return
+ }
+
+ //
+ // Part 4: Get essential data off the FaceTecSessionResult
+ //
+ var parameters: [String : Any] = [:]
+ parameters["faceScan"] = sessionResult.faceScanBase64
+ parameters["auditTrailImage"] = sessionResult.auditTrailCompressedBase64![0]
+ parameters["lowQualityAuditTrailImage"] = sessionResult.lowQualityAuditTrailCompressedBase64![0]
+ parameters["externalDatabaseRefID"] = self.options["id"] as! String
+
+ //
+ // Part 5: Make the Networking Call to Your Servers. Below is just example code, you are free to customize based on how your own API works.
+ //
+ let request = NSMutableURLRequest(url: NSURL(string: ZoomGlobalState.ZoomServerBaseURL + "/enrollment-3d")! as URL)
+ request.httpMethod = "POST"
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions(rawValue: 0))
+ request.addValue(licenseKey, forHTTPHeaderField: "X-Device-Key")
+ request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(sessionResult.sessionId), forHTTPHeaderField: "User-Agent")
+
+ let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
+ latestNetworkRequest = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
+ //
+ // Part 6: In our Sample, we evaluate a boolean response and treat true as success, false as "User Needs to Retry",
+ // and handle all other non-nominal responses by cancelling out. You may have different paradigms in your own API and are free to customize based on these.
+ //
+
+ guard let data = data else {
+ // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error.
+ faceScanResultCallback.onFaceScanResultCancel()
+ return
+ }
+
+ guard let responseJSON = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] else {
+ // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error.
+ faceScanResultCallback.onFaceScanResultCancel()
+ return
+ }
+
+ //
+ // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor.
+ // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like.
+ //
+// self.fromViewController.setLatestServerResult(responseJSON: responseJSON)
+
+ let didSucceed = responseJSON["success"] as? Bool
+
+ if didSucceed == true {
+ // CASE: Success! User successfully enrolled.
+
+ self.isSuccess = true;
+
+ // TODO: make this customizable
+ FaceTecCustomization.setOverrideResultScreenSuccessMessage("Enrollment\nSucceeded")
+ faceScanResultCallback.onFaceScanResultSucceed()
+ }
+ else if (didSucceed == false) {
+ // CASE: In our Sample code, "success" being present and false means that the User Needs to Retry.
+ // Real Users will likely succeed on subsequent attempts after following on-screen guidance.
+ // Attackers/Fraudsters will continue to get rejected.
+ faceScanResultCallback.onFaceScanResultRetry()
+ }
+ else {
+ // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error.
+ faceScanResultCallback.onFaceScanResultCancel()
+ }
+ })
+
+ //
+ // Part 8: Actually send the request.
+ //
+ latestNetworkRequest.resume()
+
+ //
+ // Part 9: For better UX, update the User if the upload is taking a while. You are free to customize and enhance this behavior to your liking.
+ //
+ DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
+ if self.latestNetworkRequest.state == .completed { return }
+
+ let uploadMessage:NSMutableAttributedString = NSMutableAttributedString.init(string: "Still Uploading...")
+ faceScanResultCallback.onFaceScanUploadMessageOverride(uploadMessageOverride: uploadMessage)
+ }
+ }
+
+ //
+ // URLSessionTaskDelegate function to get progress while performing the upload to update the UX.
+ //
+ func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
+ let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
+ faceScanResultCallback.onFaceScanUploadProgress(uploadedPercent: uploadProgress)
+ }
+
+ //
+ // Part 10: This function gets called after the FaceTec SDK is completely done. There are no parameters because you have already been passed all data in the processSessionWhileFaceTecSDKWaits function and have already handled all of your own results.
+ //
+ func onFaceTecSDKCompletelyDone() {
+ delegate.onProcessingComplete(isSuccess: isSuccess, facetecSessionResult: latestZoomSessionResult)
+ }
+}
diff --git a/ios/Helpers/NetworkingHelpers.swift b/ios/Helpers/NetworkingHelpers.swift
index 5a98f4d..82951a9 100644
--- a/ios/Helpers/NetworkingHelpers.swift
+++ b/ios/Helpers/NetworkingHelpers.swift
@@ -2,7 +2,7 @@
import UIKit
import Foundation
-import ZoomAuthentication
+import FaceTecSDK
// Possible directives after parsing the result from ZoOm Server
enum UXNextStep {
@@ -13,14 +13,14 @@ enum UXNextStep {
class NetworkingHelpers {
// Set up common parameters needed to communicate to the API.
- class func getCommonParameters(zoomSessionResult: ZoomSessionResult) -> [String : Any] {
- let zoomFaceMapBase64 = zoomSessionResult.faceMetrics?.faceMapBase64;
+ class func getCommonParameters(zoomSessionResult: FaceTecSessionResult) -> [String : Any] {
+ let zoomFaceMapBase64 = zoomSessionResult.faceScanBase64;
var parameters: [String : Any] = [:]
parameters["faceMap"] = zoomFaceMapBase64
parameters["sessionId"] = zoomSessionResult.sessionId
- if let auditTrail = zoomSessionResult.faceMetrics?.auditTrailCompressedBase64 {
+ if let auditTrail = zoomSessionResult.auditTrailCompressedBase64 {
parameters["auditTrailImage"] = auditTrail[0]
}
@@ -76,7 +76,7 @@ class NetworkingHelpers {
// Required parameters to interact with the FaceTec Managed Testing API.
// request.addValue(ZoomGlobalState.DeviceLicenseKeyIdentifier, forHTTPHeaderField: "X-Device-License-Key")
request.addValue(licenseKey, forHTTPHeaderField: "X-Device-License-Key")
- request.addValue(Zoom.sdk.createZoomAPIUserAgentString(sessionId), forHTTPHeaderField: "User-Agent")
+ request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(sessionId), forHTTPHeaderField: "User-Agent")
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: urlSessionDelegate, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
@@ -100,13 +100,17 @@ class NetworkingHelpers {
public class func getLivenessCheckResponseFromZoomServer(
urlSessionDelegate: URLSessionDelegate,
licenseKey: String,
- zoomSessionResult: ZoomSessionResult,
+ zoomSessionResult: FaceTecSessionResult,
resultCallback: @escaping (UXNextStep) -> ()
)
{
- let parameters = getCommonParameters(zoomSessionResult: zoomSessionResult)
+ var parameters: [String : Any] = [:]
+ parameters["faceScan"] = zoomSessionResult.faceScanBase64
+ parameters["auditTrailImage"] = zoomSessionResult.auditTrailCompressedBase64![0]
+ parameters["lowQualityAuditTrailImage"] = zoomSessionResult.lowQualityAuditTrailCompressedBase64![0]
+
callToZoomServerForResult(
- endpoint: ZoomGlobalState.ZoomServerBaseURL + "/liveness",
+ endpoint: ZoomGlobalState.ZoomServerBaseURL + "/liveness-3d",
parameters: parameters,
sessionId: zoomSessionResult.sessionId,
urlSessionDelegate: urlSessionDelegate,
@@ -189,10 +193,12 @@ class NetworkingHelpers {
class ServerResultHelpers {
// if Liveness was Determined, succeed. Otherwise fail. Unexpected responses cancel.
public class func getLivenessNextStep(responseJSONObj: [String: AnyObject]) -> UXNextStep {
- if (responseJSONObj["data"] as? [String : Any])?["livenessStatus"] as? Int == 0 {
+ let didSucceed = responseJSONObj["success"] as? Bool
+
+ if didSucceed == true {
return .Succeed
}
- else if (responseJSONObj["data"] as? [String : Any])?["livenessStatus"] as? Int == 1 {
+ else if (didSucceed == false) {
return .Retry
}
else {
diff --git a/ios/Helpers/ThemeHelpers.swift b/ios/Helpers/ThemeHelpers.swift
index cb9cbe7..9d78b47 100644
--- a/ios/Helpers/ThemeHelpers.swift
+++ b/ios/Helpers/ThemeHelpers.swift
@@ -8,13 +8,13 @@
import Foundation
import UIKit
-import ZoomAuthentication
+import FaceTecSDK
class ThemeHelpers {
public class func setAppTheme(theme: String) {
if theme == "ZoOm Theme" {
- ZoomGlobalState.currentCustomization = ZoomCustomization()
+ ZoomGlobalState.currentCustomization = FaceTecCustomization()
}
else if theme == "Well-Rounded" {
let primaryColor = UIColor(red: 0.035, green: 0.710, blue: 0.639, alpha: 1) // green
@@ -26,8 +26,8 @@ class ThemeHelpers {
backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0)
// Overlay Customization
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .styleLight
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0.8
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .styleLight
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0.8
ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.clear
ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage.init()
// Guidance Customization
@@ -42,7 +42,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 30
ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2)
// Guidance Image Customization
- ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_green")
+// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_green")
// ID Scan Customization
ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage.init()
ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = false
@@ -66,8 +66,8 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 2
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 5
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_green")
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_green")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_green")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_green")
// Result Screen Customization
ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor]
ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor
@@ -91,7 +91,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor
// Cancel Button Customization
ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "cancel_round_green")
- ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_round_green")
+// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_round_green")
}
else if theme == "Dark-Side" {
let primaryColor = UIColor(red: 0, green: 0.675, blue: 0.929, alpha: 1) // blue
@@ -103,8 +103,8 @@ class ThemeHelpers {
backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0)
// Overlay Customization
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.black
ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage.init()
// Guidance Customization
@@ -119,7 +119,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 2
ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2)
// Guidance Image Customization
- ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_blue")
+// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_blue")
// ID Scan Customization
ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage.init()
ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = false
@@ -143,8 +143,8 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 2
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 8
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_blue")
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_blue")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_blue")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_blue")
// Result Screen Customization
ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor]
ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor
@@ -168,7 +168,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor.withAlphaComponent(0.5)
// Cancel Button Customization
ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "double_chevron_left_blue")
- ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "double_chevron_left_blue")
+// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "double_chevron_left_blue")
}
else if theme == "Bitcoin Exchange" {
let primaryColor = UIColor(red: 0.969, green: 0.588, blue: 0.204, alpha: 1) // orange
@@ -181,8 +181,8 @@ class ThemeHelpers {
backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0)
// Overlay Customization
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.white
ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage(named: "bitcoin_exchange_logo")
// Guidance Customization
@@ -197,7 +197,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 5
ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2)
// Guidance Image Customization
- ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_orange")
+// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_orange")
// ID Scan Customization
ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage.init()
ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = false
@@ -221,8 +221,8 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 0
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 8
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_orange")
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_orange")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_orange")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_orange")
// Result Screen Customization
ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor]
ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor
@@ -246,7 +246,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = secondaryColor
// Cancel Button Customization
ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "back_orange")
- ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "back_orange")
+// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "back_orange")
}
else if theme == "eKYC" {
let primaryColor = UIColor(red: 0.929, green: 0.110, blue: 0.141, alpha: 1) // red
@@ -259,8 +259,8 @@ class ThemeHelpers {
backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0)
// Overlay Customization
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.clear
ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage(named: "ekyc_logo")
// Guidance Customization
@@ -275,7 +275,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 8
ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2)
// Guidance Image Customization
- ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_red")
+// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_red")
// ID Scan Customization
ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage(named: "ekyc_logo")
ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = true
@@ -299,8 +299,8 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 0
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 2
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_red")
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_red")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_red")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_red")
// Result Screen Customization
ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor]
ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = secondaryColor
@@ -324,7 +324,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor.withAlphaComponent(0.5)
// Cancel Button Customization
ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "cancel_box_red")
- ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_box_red")
+// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_box_red")
}
else if theme == "Sample Bank" {
let primaryColor = UIColor.white
@@ -337,8 +337,8 @@ class ThemeHelpers {
backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0)
// Overlay Customization
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
- ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off
+// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0
ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = primaryColor
ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage(named: "sample_bank_logo")
// Guidance Customization
@@ -353,7 +353,7 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 2
ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2)
// Guidance Image Customization
- ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_white_navy")
+// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_white_navy")
// ID Scan Customization
ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage(named: "sample_bank_logo")
ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = true
@@ -377,8 +377,8 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = backgroundColor
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 2
ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 2
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_navy")
- ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_navy")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_navy")
+// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_navy")
// Result Screen Customization
ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor]
ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor
@@ -402,10 +402,10 @@ class ThemeHelpers {
ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor.withAlphaComponent(0.5)
// Cancel Button Customization
ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "cancel_white")
- ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_navy")
+// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_navy")
}
- Zoom.sdk.setCustomization(ZoomGlobalState.currentCustomization)
+ FaceTec.sdk.setCustomization(ZoomGlobalState.currentCustomization)
}
}
diff --git a/ios/LivenessCheckProcessor.swift b/ios/LivenessCheckProcessor.swift
index ee79c81..0cd6e46 100644
--- a/ios/LivenessCheckProcessor.swift
+++ b/ios/LivenessCheckProcessor.swift
@@ -2,15 +2,15 @@
import UIKit
import Foundation
-import ZoomAuthentication
+import FaceTecSDK
-class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessorDelegate, ZoomSessionDelegate {
+class LivenessCheckProcessor: NSObject, URLSessionDelegate, FaceTecFaceScanProcessorDelegate {
var licenseKey: String!
var delegate: ProcessingDelegate
- var latestZoomSessionResult: ZoomSessionResult?
+ var latestZoomSessionResult: FaceTecSessionResult?
var isSuccess: Bool
- init(delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) {
+ init(sessionToken: String, delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) {
self.delegate = delegate
self.isSuccess = false
@@ -19,8 +19,8 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor
self.licenseKey = options["licenseKey"] as? String
// Launch the ZoOm Session.
- let sessionVC = Zoom.sdk.createSessionVC(delegate: self, faceMapProcessorDelegate: self)
- if (options["useOverlay"] as? Bool)! {
+ let sessionVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken)
+ if (options["useOverlay"] as? Bool ?? false) {
sessionVC.modalPresentationStyle = .overCurrentContext
}
@@ -28,11 +28,11 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor
}
// Required function that handles calling ZoOm Server to get result and decides how to continue.
- func processZoomSessionResultWhileZoomWaits(zoomSessionResult: ZoomSessionResult, zoomFaceMapResultCallback: ZoomFaceMapResultCallback) {
- self.latestZoomSessionResult = zoomSessionResult
+ func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) {
+ self.latestZoomSessionResult = sessionResult
// cancellation, timeout, etc.
- if zoomSessionResult.status != .sessionCompletedSuccessfully || zoomSessionResult.faceMetrics?.faceMap == nil {
- zoomFaceMapResultCallback.onFaceMapResultCancel();
+ if sessionResult.status != .sessionCompletedSuccessfully {
+ faceScanResultCallback.onFaceScanResultCancel();
return
}
@@ -40,19 +40,19 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor
NetworkingHelpers.getLivenessCheckResponseFromZoomServer(
urlSessionDelegate: self,
licenseKey: self.licenseKey,
- zoomSessionResult: zoomSessionResult,
+ zoomSessionResult: sessionResult,
resultCallback: { nextStep in
if nextStep == .Succeed {
// Dynamically set the success message.
// ZoomCustomization.setOverrideResultScreenSuccessMessage("Liveness\nConfirmed")
- zoomFaceMapResultCallback.onFaceMapResultSucceed()
+ faceScanResultCallback.onFaceScanResultSucceed()
self.isSuccess = true
}
else if nextStep == .Retry {
- zoomFaceMapResultCallback.onFaceMapResultRetry()
+ faceScanResultCallback.onFaceScanResultRetry()
}
else {
- zoomFaceMapResultCallback.onFaceMapResultCancel()
+ faceScanResultCallback.onFaceScanResultCancel()
}
}
)
@@ -64,7 +64,7 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor
}
// The final callback ZoOm SDK calls when done with everything.
- func onZoomSessionComplete() {
- delegate.onProcessingComplete(isSuccess: isSuccess, zoomSessionResult: latestZoomSessionResult)
+ func onFaceTecSDKCompletelyDone() {
+ delegate.onProcessingComplete(isSuccess: isSuccess, facetecSessionResult: latestZoomSessionResult)
}
}
diff --git a/ios/ProcesingDelegate.swift b/ios/ProcesingDelegate.swift
index fbdf229..6af707c 100644
--- a/ios/ProcesingDelegate.swift
+++ b/ios/ProcesingDelegate.swift
@@ -1,8 +1,8 @@
// Helpful interfaces and enums
-import ZoomAuthentication
+import FaceTecSDK
protocol ProcessingDelegate: class {
- func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?)
- func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?, zoomIDScanResult: ZoomIDScanResult?)
+ func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?)
+ func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?, facetecIDScanResult: FaceTecIDScanResult?)
}
diff --git a/ios/RNReactNativeZoomSdk.m b/ios/RNReactNativeZoomSdk.m
index 03950e0..b087344 100644
--- a/ios/RNReactNativeZoomSdk.m
+++ b/ios/RNReactNativeZoomSdk.m
@@ -14,8 +14,6 @@
@interface RCT_EXTERN_REMAP_MODULE(RNReactNativeZoomSdk, ZoomAuth, RCTViewManager)
-RCT_EXTERN_METHOD(preload)
-
RCT_EXTERN_METHOD(getVersion:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
@@ -23,7 +21,15 @@ @interface RCT_EXTERN_REMAP_MODULE(RNReactNativeZoomSdk, ZoomAuth, RCTViewManage
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
-RCT_EXTERN_METHOD(verify:(NSDictionary*) options
+RCT_EXTERN_METHOD(verifyLiveness:(NSDictionary*) options
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject)
+
+RCT_EXTERN_METHOD(enroll:(NSDictionary*) options
+ resolver:(RCTPromiseResolveBlock)resolve
+ rejecter:(RCTPromiseRejectBlock)reject)
+
+RCT_EXTERN_METHOD(authenticate:(NSDictionary*) options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
diff --git a/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj b/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj
index e2c90b3..1b59c30 100644
--- a/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj
+++ b/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj
@@ -8,11 +8,13 @@
/* Begin PBXBuildFile section */
85B6B0F92018C5EA00A70FDA /* ZoomAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B6B0F82018C5EA00A70FDA /* ZoomAuth.swift */; };
+ AE87D795253DFE44007D0024 /* EnrollmentProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE87D794253DFE44007D0024 /* EnrollmentProcessor.swift */; };
AE9D6FBE23C165CF00B12F1C /* LivenessCheckProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */; };
AE9D6FC023C1669C00B12F1C /* ProcesingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FBF23C1669C00B12F1C /* ProcesingDelegate.swift */; };
AE9D6FC323C16E0A00B12F1C /* ZoomGlobalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FC223C16E0A00B12F1C /* ZoomGlobalState.swift */; };
AE9D6FC523C16EA500B12F1C /* ThemeHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FC423C16EA500B12F1C /* ThemeHelpers.swift */; };
AE9D6FC723C16EA600B12F1C /* NetworkingHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FC623C16EA600B12F1C /* NetworkingHelpers.swift */; };
+ AEC3CD0B254A15740087A0CB /* AuthenticateProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC3CD0A254A15740087A0CB /* AuthenticateProcessor.swift */; };
B3E7B58A1CC2AC0600A0062D /* RNReactNativeZoomSdk.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNReactNativeZoomSdk.m */; };
/* End PBXBuildFile section */
@@ -33,11 +35,13 @@
85B6B0F72018C5E900A70FDA /* RNReactNativeZoomSdk-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNReactNativeZoomSdk-Bridging-Header.h"; sourceTree = ""; };
85B6B0F82018C5EA00A70FDA /* ZoomAuth.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ZoomAuth.swift; sourceTree = ""; tabWidth = 2; };
85B6B0FD2018C6A300A70FDA /* ZoomConfigurationForTestApplication.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ZoomConfigurationForTestApplication.xcconfig; sourceTree = ""; };
+ AE87D794253DFE44007D0024 /* EnrollmentProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollmentProcessor.swift; sourceTree = ""; };
AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LivenessCheckProcessor.swift; sourceTree = ""; };
AE9D6FBF23C1669C00B12F1C /* ProcesingDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcesingDelegate.swift; sourceTree = ""; };
AE9D6FC223C16E0A00B12F1C /* ZoomGlobalState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZoomGlobalState.swift; sourceTree = ""; };
AE9D6FC423C16EA500B12F1C /* ThemeHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ThemeHelpers.swift; path = Helpers/ThemeHelpers.swift; sourceTree = ""; };
AE9D6FC623C16EA600B12F1C /* NetworkingHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NetworkingHelpers.swift; path = Helpers/NetworkingHelpers.swift; sourceTree = ""; };
+ AEC3CD0A254A15740087A0CB /* AuthenticateProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticateProcessor.swift; sourceTree = ""; };
B3E7B5881CC2AC0600A0062D /* RNReactNativeZoomSdk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNReactNativeZoomSdk.h; sourceTree = ""; };
B3E7B5891CC2AC0600A0062D /* RNReactNativeZoomSdk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNReactNativeZoomSdk.m; sourceTree = ""; };
/* End PBXFileReference section */
@@ -64,6 +68,9 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
+ AEC3CD0A254A15740087A0CB /* AuthenticateProcessor.swift */,
+ AE87D794253DFE44007D0024 /* EnrollmentProcessor.swift */,
+ AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */,
85B6B0F82018C5EA00A70FDA /* ZoomAuth.swift */,
AE9D6FC623C16EA600B12F1C /* NetworkingHelpers.swift */,
AE9D6FC423C16EA500B12F1C /* ThemeHelpers.swift */,
@@ -71,7 +78,6 @@
B3E7B5881CC2AC0600A0062D /* RNReactNativeZoomSdk.h */,
B3E7B5891CC2AC0600A0062D /* RNReactNativeZoomSdk.m */,
85B6B0FD2018C6A300A70FDA /* ZoomConfigurationForTestApplication.xcconfig */,
- AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */,
AE9D6FBF23C1669C00B12F1C /* ProcesingDelegate.swift */,
134814211AA4EA7D00B7C361 /* Products */,
85B6B0F72018C5E900A70FDA /* RNReactNativeZoomSdk-Bridging-Header.h */,
@@ -144,10 +150,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ AEC3CD0B254A15740087A0CB /* AuthenticateProcessor.swift in Sources */,
AE9D6FC723C16EA600B12F1C /* NetworkingHelpers.swift in Sources */,
AE9D6FC323C16E0A00B12F1C /* ZoomGlobalState.swift in Sources */,
AE9D6FC523C16EA500B12F1C /* ThemeHelpers.swift in Sources */,
85B6B0F92018C5EA00A70FDA /* ZoomAuth.swift in Sources */,
+ AE87D795253DFE44007D0024 /* EnrollmentProcessor.swift in Sources */,
AE9D6FBE23C165CF00B12F1C /* LivenessCheckProcessor.swift in Sources */,
B3E7B58A1CC2AC0600A0062D /* RNReactNativeZoomSdk.m in Sources */,
AE9D6FC023C1669C00B12F1C /* ProcesingDelegate.swift in Sources */,
@@ -180,7 +188,6 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
- "$(SRCROOT)/../../../ios/Frameworks",
"$(SRCROOT)/../../../ios",
"$(SRCROOT)/../../../iOS",
);
@@ -229,7 +236,6 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
- "$(SRCROOT)/../../../ios/Frameworks",
"$(SRCROOT)/../../../ios",
"$(SRCROOT)/../../../iOS",
);
diff --git a/ios/ZoomAuth.swift b/ios/ZoomAuth.swift
index 3e7ca8e..7899c3b 100644
--- a/ios/ZoomAuth.swift
+++ b/ios/ZoomAuth.swift
@@ -7,13 +7,14 @@
//
import UIKit
-import ZoomAuthentication
+import FaceTecSDK
+import Network
@objc(ZoomAuth)
-class ZoomAuth: RCTViewManager, ProcessingDelegate {
+class ZoomAuth: RCTViewManager, ProcessingDelegate, URLSessionTaskDelegate {
- var verifyResolver: RCTPromiseResolveBlock? = nil
- var verifyRejecter: RCTPromiseRejectBlock? = nil
+ var resolver: RCTPromiseResolveBlock? = nil
+ var rejecter: RCTPromiseRejectBlock? = nil
var returnBase64: Bool = false
var initialized = false
var licenseKey: String!
@@ -25,23 +26,57 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate {
}
// React Method
- @objc func verify(_ options: Dictionary, // options not used at the moment
+ @objc func verifyLiveness(_ options: Dictionary, // options not used at the moment
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
- self.verifyResolver = resolve
- self.verifyRejecter = reject
- self.returnBase64 = (options["returnBase64"] as? Bool)!
+ self.resolver = resolve
+ self.rejecter = reject
+ self.returnBase64 = options["returnBase64"] as? Bool ?? false;
DispatchQueue.main.async {
let root = UIApplication.shared.keyWindow!.rootViewController!
var optionsWithKey = options
optionsWithKey["licenseKey"] = self.licenseKey
- let _ = LivenessCheckProcessor(delegate: self, fromVC: root, options: optionsWithKey)
+ self.getSessionToken() { sessionToken in
+ let _ = LivenessCheckProcessor(sessionToken: sessionToken, delegate: self, fromVC: root, options: optionsWithKey)
+ }
+ }
+ }
+
+ // React Method
+ @objc func enroll(_ options: Dictionary, // options not used at the moment
+ resolver resolve: @escaping RCTPromiseResolveBlock,
+ rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
+ self.resolver = resolve
+ self.rejecter = reject
+ DispatchQueue.main.async {
+ let root = UIApplication.shared.keyWindow!.rootViewController!
+ var optionsWithKey = options
+ optionsWithKey["licenseKey"] = self.licenseKey
+ self.getSessionToken() { sessionToken in
+ let _ = EnrollmentProcessor(sessionToken: sessionToken, delegate: self, fromVC: root, options: optionsWithKey)
+ }
+ }
+ }
+
+ // React Method
+ @objc func authenticate(_ options: Dictionary, // options not used at the moment
+ resolver resolve: @escaping RCTPromiseResolveBlock,
+ rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
+ self.resolver = resolve
+ self.rejecter = reject
+ DispatchQueue.main.async {
+ let root = UIApplication.shared.keyWindow!.rootViewController!
+ var optionsWithKey = options
+ optionsWithKey["licenseKey"] = self.licenseKey
+ self.getSessionToken() { sessionToken in
+ let _ = AuthenticateProcessor(sessionToken: sessionToken, delegate: self, fromVC: root, options: optionsWithKey)
+ }
}
}
// Show the final result and transition back into the main interface.
- func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?) {
- let statusCode = zoomSessionResult?.status.rawValue ?? -1
+ func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?) {
+ let statusCode = facetecSessionResult?.status.rawValue ?? -1
var resultJson:[String:Any] = [
"success": isSuccess,
"status": statusCode
@@ -52,99 +87,41 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate {
return
}
- resultJson["countOfZoomSessionsPerformed"] = zoomSessionResult?.countOfZoomSessionsPerformed ?? 1
- resultJson["sessionId"] = zoomSessionResult?.sessionId ?? ""
+ resultJson["sessionId"] = facetecSessionResult?.sessionId ?? ""
- if zoomSessionResult?.faceMetrics == nil {
- self.sendResult(resultJson)
- return
+ if self.returnBase64 && facetecSessionResult?.faceScan != nil {
+ resultJson["facemapBase64"] = facetecSessionResult!.faceScanBase64
}
- let face = zoomSessionResult?.faceMetrics
- var faceMetrics: [String:Any] = [:]
- if self.returnBase64 && face?.faceMapBase64 != nil {
- faceMetrics["facemap"] = face!.faceMapBase64
- }
-
-// resultJson["faceMetrics"] = faceMetrics
- if face?.auditTrail == nil {
- self.sendResult(resultJson)
- return
- }
-
- let auditTrailImages = face!.auditTrail!
- var auditTrail:[String] = []
- if self.returnBase64 {
- if let auditTrailBase64 = face?.auditTrailCompressedBase64 {
- faceMetrics["auditTrail"] = auditTrailBase64
- }
-
- resultJson["faceMetrics"] = faceMetrics
- self.sendResult(resultJson)
- return
- }
-
- var togo = face?.auditTrailCompressedBase64?.count ?? 0
- if let faceMap = face?.faceMap {
- togo += 1
- storeDataInImageStore(faceMap) { (tag) in
- faceMetrics["facemap"] = tag
- togo -= 1
- if togo == 0 {
- resultJson["faceMetrics"] = faceMetrics
- self.sendResult(resultJson)
- }
- }
- }
-
- for image in auditTrailImages {
- uiImageToImageStoreKey(image) { (tag) in
- if (tag != nil) {
- auditTrail.append(tag!)
- }
-
- togo -= 1
- if togo == 0 {
- faceMetrics["auditTrail"] = auditTrail
- resultJson["faceMetrics"] = faceMetrics
- self.sendResult(resultJson)
- }
- }
- }
-
-// EXAMPLE: retrieve facemap
-// if let zoomFacemap = result.faceMetrics?.zoomFacemap {
-// // handle ZoOm Facemap
-// }
-
+ self.sendResult(resultJson)
}
// Show the final result and transition back into the main interface.
- func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?, zoomIDScanResult: ZoomIDScanResult?) {
+ func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?, facetecIDScanResult: FaceTecIDScanResult?) {
}
func sendResult(_ result: [String:Any]) -> Void {
- if (self.verifyResolver == nil) {
+ if (self.resolver == nil) {
return
}
- self.verifyResolver!(result)
+ self.resolver!(result)
self.cleanUp()
}
// not used at the moment
func sendError(_ code: String, message: String, error: Error) -> Void {
- if (self.verifyRejecter == nil) {
+ if (self.rejecter == nil) {
return
}
- self.verifyRejecter!(code, message, error)
+ self.rejecter!(code, message, error)
self.cleanUp()
}
func cleanUp () -> Void {
- self.verifyResolver = nil
- self.verifyRejecter = nil
+ self.resolver = nil
+ self.rejecter = nil
}
func uiImageToBase64 (_ image: UIImage) -> String {
@@ -164,16 +141,11 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate {
imageStore.storeImageData(data, with: completionHandler)
}
- // React Method
- @objc func preload() -> Void {
- Zoom.sdk.preload()
- }
-
// React Method
@objc func getVersion(_ resolve: RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock) -> Void {
- let result: String = Zoom.sdk.version
+ let result: String = FaceTec.sdk.version
if ( !result.isEmpty ) {
resolve([
@@ -190,34 +162,22 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate {
@objc func initialize(_ options: Dictionary,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
- self.licenseKey = options["licenseKey"] as? String
+ self.licenseKey = options["deviceKeyIdentifier"] as! String
- if (options["facemapEncryptionKey"] != nil) {
- let publicKey = options["facemapEncryptionKey"] as! String
- Zoom.sdk.setFaceMapEncryptionKey(publicKey: publicKey);
- }
+ let faceMapEncryptionKey = options["facemapEncryptionKey"] as! String
- Zoom.sdk.auditTrailType = .height640 // otherwise no auditTrail images
+ FaceTec.sdk.auditTrailType = .height640 // otherwise no auditTrail images
// Create the customization object
- let currentCustomization: ZoomCustomization = ZoomCustomization()
+ let currentCustomization: FaceTecCustomization = FaceTecCustomization()
// disable the "Your App Logo" section
currentCustomization.overlayCustomization.brandingImage = nil
-// currentCustomization.overlayCustomization.blurEffectOpacity = 0
-// currentCustomization.frameCustomization.blurEffectOpacity = 0
-// currentCustomization.guidanceCustomization.showIntroScreenBrandingImage = false
-
- // Sample UI Customization: vertically center the ZoOm frame within the device's display
-// if (options["centerFrame"] as? Bool)! {
-// centerZoomFrameCustomization(currentZoomCustomization: currentCustomization)
-// } else if (options["topMargin"] != nil) {
-// currentCustomization.frameCustomization.topMargin = options["topMargin"] as! Int32
-// }
// Apply the customization changes
- Zoom.sdk.setCustomization(currentCustomization)
- Zoom.sdk.initialize(
- licenseKeyIdentifier: options["licenseKey"] as! String,
+ FaceTec.sdk.setCustomization(currentCustomization)
+ FaceTec.sdk.initializeInDevelopmentMode(
+ deviceKeyIdentifier: licenseKey,
+ faceScanEncryptionKey: faceMapEncryptionKey,
completion: { (licenseKeyValidated: Bool) -> Void in
//
// We want to ensure that licenseKey is valid before enabling verification
@@ -231,31 +191,45 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate {
])
}
else {
- let status = Zoom.sdk.getStatus().rawValue
+ let status = FaceTec.sdk.getStatus().rawValue
resolve([
"success": false,
"status": status
])
-
-// let errorMsg = "AppToken did not validate. If Zoom ViewController's are launched, user will see an app token error state"
-// print(errorMsg)
-// let err: NSError = NSError(domain: errorMsg, code: 0, userInfo: nil)
-// reject("initialize", errorMsg, err)
}
}
)
}
-// func centerZoomFrameCustomization(currentZoomCustomization: ZoomCustomization) {
-// let screenHeight: CGFloat = UIScreen.main.fixedCoordinateSpace.bounds.size.height
-// var frameHeight: CGFloat = screenHeight * CGFloat(currentZoomCustomization.frameCustomization.sizeRatio)
-// // Detect iPhone X and iPad displays
-// if UIScreen.main.fixedCoordinateSpace.bounds.size.height >= 812 {
-// let screenWidth = UIScreen.main.fixedCoordinateSpace.bounds.size.width
-// frameHeight = screenWidth * (16.0/9.0) * CGFloat(currentZoomCustomization.frameCustomization.sizeRatio)
-// }
-// let topMarginToCenterFrame = (screenHeight - frameHeight)/2.0
-//
-// currentZoomCustomization.frameCustomization.topMargin = Int32(topMarginToCenterFrame)
-// }
+ func getSessionToken(sessionTokenCallback: @escaping (String) -> ()) {
+ let endpoint = ZoomGlobalState.ZoomServerBaseURL + "/session-token"
+ let request = NSMutableURLRequest(url: NSURL(string: endpoint)! as URL)
+ request.httpMethod = "GET"
+ // Required parameters to interact with the FaceTec Managed Testing API.
+ request.addValue(self.licenseKey, forHTTPHeaderField: "X-Device-Key")
+ request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(""), forHTTPHeaderField: "User-Agent")
+
+ let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
+ let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
+ // Ensure the data object is not nil otherwise callback with empty dictionary.
+ guard let data = data else {
+ print("Exception raised while attempting HTTPS call.")
+// self.handleErrorGettingServerSessionToken()
+ return
+ }
+ if let responseJSONObj = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject] {
+ if((responseJSONObj["sessionToken"] as? String) != nil)
+ {
+// self.hideSessionTokenConnectionText()
+ sessionTokenCallback(responseJSONObj["sessionToken"] as! String)
+ return
+ }
+ else {
+ print("Exception raised while attempting HTTPS call.")
+// self.handleErrorGettingServerSessionToken()
+ }
+ }
+ })
+ task.resume()
+ }
}
diff --git a/ios/ZoomGlobalState.swift b/ios/ZoomGlobalState.swift
index dfb6070..46b0af3 100644
--- a/ios/ZoomGlobalState.swift
+++ b/ios/ZoomGlobalState.swift
@@ -1,30 +1,13 @@
import UIKit
import Foundation
-import ZoomAuthentication
+import FaceTecSDK
class ZoomGlobalState {
- // "https://api.zoomauth.com/api/v2/biometrics" for FaceTec Managed Testing API.
+ // "https://api.zoomauth.com/api/v3/biometrics" for FaceTec Managed Testing API.
// "http://localhost:8080" if running ZoOm Server SDK (Dockerized) locally.
// Otherwise, your webservice URL.
- static let ZoomServerBaseURL = "https://api.zoomauth.com/api/v2/biometrics"
+ static let ZoomServerBaseURL = "https://api.facetec.com/api/v3/biometrics"
- // The customer-controlled public key used during encryption of FaceMap data.
- // Please see https://dev.zoomlogin.com/zoomsdk/#/licensing-and-encryption-keys for more information.
- static let PublicFaceMapEncryptionKey =
- "-----BEGIN PUBLIC KEY-----\n" +
- "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5PxZ3DLj+zP6T6HFgzzk\n" +
- "M77LdzP3fojBoLasw7EfzvLMnJNUlyRb5m8e5QyyJxI+wRjsALHvFgLzGwxM8ehz\n" +
- "DqqBZed+f4w33GgQXFZOS4AOvyPbALgCYoLehigLAbbCNTkeY5RDcmmSI/sbp+s6\n" +
- "mAiAKKvCdIqe17bltZ/rfEoL3gPKEfLXeN549LTj3XBp0hvG4loQ6eC1E1tRzSkf\n" +
- "GJD4GIVvR+j12gXAaftj3ahfYxioBH7F7HQxzmWkwDyn3bqU54eaiB7f0ftsPpWM\n" +
- "ceUaqkL2DZUvgN0efEJjnWy5y1/Gkq5GGWCROI9XG/SwXJ30BbVUehTbVcD70+ZF\n" +
- "8QIDAQAB\n" +
- "-----END PUBLIC KEY-----"
-
- // Used for bookkeeping around demonstrating enrollment/authentication functionality of ZoOm
-// static var randomUsername: String = ""
-// static var isRandomUsernameEnrolled = false
-
// this app can modify the customization to demonstrate different look/feel preferences for ZoOm
- static var currentCustomization: ZoomCustomization = ZoomCustomization()
+ static var currentCustomization: FaceTecCustomization = FaceTecCustomization()
}