Installation

Prove provides the iOS SDK in Swift. It has a download size of 2.5 MB and an install size of 1.5 MB for the minimum required components. It relies on iOS native APIs. Prove’s iOS SDK supports the earlier three major versions. Prove has seen successful transactions with iOS v11. Prove manages a repository with the libraries to enable integration. Execute the following to import CocoaPod from the Prove pod repository:
shell
# Run this command to install the cocoapods-art plugin (authored by Artifactory)
gem install cocoapods-art

# Run this command to add the Prove pod repository
pod repo-art add prove.jfrog.io https://prove.jfrog.io/artifactory/api/pods/libs-public-cocoapods

# In your Podfile, paste in the Prove pod repository as a source
plugin 'cocoapods-art', :sources => [
    'prove.jfrog.io'
]

# In your Podfile, paste in the SDK pods
pod 'ProveAuth', '6.5.1'

# Run this command to install the SDK pods
pod install

Send the Type of Flow: Mobile

Unlike the Web SDK, when using the iOS SDK, use the mobile flow. Pass mobile to the Unify() function on the server. In a mobile flow, the mobile phone performs one-time password (OTP) validation. In the mobile flow, once OTP validation is complete, the AuthFinishStep function executes.

Authenticate()

The SDK requires an authToken as a parameter for the Authenticate() function. This token returns from the Unify() call of the server-side SDK. The token is session specific so it’s used for a single flow. It also expires after 15 minutes.

Retrieve authToken

To start the flow, send a request to your back-end server with the possession type. Include a phone number if you are using Prove’s possession check.
Swift
// The below example uses native iOS URLSession, but any other
// alternative networking approaches should also work
func initialize(phoneNumber: String, possessionType: String, completion: @escaping (Result\<String, Error>) -> Void) {
    guard let url = URL(string: "\(backendUrl)/initialize") else {
        completion(.failure(URLError(.badURL)))
        return
    }
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    // Set up the request body
    let body: [String: Any] = [
        "phoneNumber": phoneNumber,
        "possessionType": possessionType
    ]
    do {
        request.httpBody = try JSONSerialization.data(withJSONObject: body, options: \[])
    } catch {
        completion(.failure(error))
        return
    }
    // Perform the request
    let task = URLSession.shared.dataTask(with: request) { \_, response, error in
        // Handle network or connection errors
        if let error = error {
            completion(.failure(error))
            return
        }
        do {
            if let json = try JSONSerialization.jsonObject(with: data, options: \[]) as? [String: Any],
               let authToken = json["authToken"] as? String {
                completion(.success(authToken))
            } else {
                let parsingError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Failed to parse JSON or authToken is missing"])
                completion(.failure(parsingError))
            }
        } catch {
            completion(.failure(error))
        }
    }
    // Start the network call
    task.resume()
}

Setup Authenticator

Once you have the authToken, build the authenticator for the mobile flow.
Swift
// Object implementing ProveAuthFinishStep protocols
let finishStep = FinishAuthStep()

// Objects implementing OtpStartStep/OtpFinishStep protocols
let otpStartStep = MobileOtpStartStep()
let otpFinishStep = MobileOtpFinishStep()

let proveAuthSdk: ProveAuth
proveAuthSdk = ProveAuth.builder(authFinish: finishStep)
    .withOtpFallback(otpStart: otpStartStep, otpFinish: otpFinishStep)
    .build()
In the event a cellular data connection is unavailable during testing, use the Builder class. It permits simulated successful session results while connected to a Wi-Fi network. Testing using a Wi-Fi connection is useful in the Sandbox environment.
Swift
proveAuthSdk = ProveAuth.builder(authFinish: finishStep)
  .withMobileAuthTestMode() // Test mode flag
  .build()

Performing the Authentication

The Prove Auth object is thread safe and used as a singleton. Most Prove Auth methods are blocking and therefore can’t execute in the main application thread. The application employs an executor service with a minimum of two threads to manage threads due to the SDK’s ability to process concurrent blocking requests.
Swift
// authToken retrieved from your server via StartAuthRequest
proveAuthSdk.authenticate(authToken) { error in
  DispatchQueue.main.async {
    self.messages.finalResultMessage = "ProveAuth.authenticate returned error: \(error.localizedDescription)"
    print(self.messages.finalResultMessage)
  }
}

Validate the Mobile Phone

In the AuthFinishStep, you’ll specify a function to call once the possession checks complete on the mobile phone. In the following code, you’ll notice an endpoint called /verify. This endpoint on your back end server calls the UnifyStatus() function to validate the phone number.
Swift
// Send a verify request.
// The below example uses native iOS URLSession, but any other
// alternative networking approaches should also work
func verify(String: authId, completion: @escaping (Error?) -> Void) {
    guard let url = URL(string: "\(backendUrl)/verify") else {
        completion(URLError(.badURL))
        return
    }
    // Create the request
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    // Set up the request body (empty JSON object) - authId is not needed so you can ignore.
    let body: [String: Any] = ["authId": authId]
    do {
        request.httpBody = try JSONSerialization.data(withJSONObject: body, options: \[])
    } catch {
        completion(error)
        return
    }
    // Perform the request
    let task = URLSession.shared.dataTask(with: request) { \_, response, error in
        if let error = error {
            completion(error)
            return
        } catch {
            completion(error)
        }
    }
    // Start the network call
    task.resume()
}

Configure OTP

To use the Resend/Retry/Phone Change features, you need to install the iOS SDK version 6.5.1 or later.
To set the One-Time Password (OTP) handler, withOtpFallback(otpStart: otpStartStep, otpFinish: otpFinishStep), requires implementing the OtpStartStep and OtpFinishStep. The default implementation is below, but you can also view the other tabs if you wish to enable advanced capabilities. The OTP session has a two minute timeout from when it’s sent through SMS to when the customer can enter in the OTP.
Follow these instructions if you are implementing OTP and you are passing in the phone number on the /v3/start endpoint. In this case, you’ve already prompted for a phone number so you don’t need to prompt for it in the client SDK.Since you passed the phone number in the Start() function, call callback.onSuccess(input: nil) to communicate to the SDK you have the customer’s agreement to deliver the SMS message.
Swift
import Foundation
import ProveAuth
import SwiftUI

class MobileOtpStartStep: OtpStartStep {
    @ObservedObject var sheetObservable: SheetObservable
    var callback: OtpStartStepCallback?

    init(sheetObservable: SheetObservable) {
        self.sheetObservable = sheetObservable
    }

    // Implement this method to handle phone number collection for SMS OTP,
    // or to obtain user confirmation for initiating an SMS message.
    func execute(
        phoneNumberNeeded: Bool, phoneValidationError: ProveAuthError?, callback: OtpStartStepCallback
    ) {
        self.callback = callback
        // Since no phone number is needed, don't prompt the user.
        callback.onSuccess(input: nil)
    }
}
Call the callback.onError() method to communicate to the SDK any issues while trying to obtain the phone number or the OTP. Report an error if the customer cancels the SMS transaction or presses the back button to leave the screen.
Swift
  import Foundation
  import SwiftUI
  import ProveAuth

class MobileOtpFinishStep: OtpFinishStep {
    @ObservedObject var sheetObservable: SheetObservable
    var callback: OtpFinishStepCallback?

    init(sheetObservable: SheetObservable){
        self.sheetObservable = sheetObservable
    }

    // Implement this method to collect the OTP value delivered via SMS.
    func execute(otpError: ProveAuthError?, callback: OtpFinishStepCallback) {
        self.callback = callback
        // Handle the OTP validation error if present.
        if case .otpValidationError = otpError {
            print("found otpError: \(String(describing: otpError?.localizedDescription))")
            // Signal to your UI components that the last provided OTP is invalid
            self.sheetObservable.otpError = true
        } else {
            self.sheetObservable.otpError = false
        }

        // Signal to UI components to display OtpFinishView
        DispatchQueue.main.async {
            self.sheetObservable.isOtpFinishActive = true
        }
    }

    // Provide the collected OTP value to the SDK for validation.
    func handleOtp(_ otp: String) {
        guard let callback = self.callback else {
            print("Error: OtpFinishStepCallback is not set ")
            return
        }

        let otpFinishInput = OtpFinishInput(otp: otp)
        callback.onSuccess(input: otpFinishInput)
    }

    // Notify the SDK of any issues encountered while obtaining the OTP value or if the user cancels the OTP flow.
    func handleOtpFinishError() {
        guard let callback = self.callback else {
            print("Error: OtpFinishStepCallback is not set ")
            return
        }

        callback.onError()
    }
}