This detailed guide explains how to create a native module for the iOS platform within a React Native application. The process follows the step-by-step instructions from the official documentation.
1. Open the iOS Project in Xcode
You can include this in your package.json under the scripts section:
"scripts": {
"open:ios": "xed ios"
},
This script ensures that the Expo module opens correctly in Xcode.
2. Create the Native Module
Create a file named CafSmartAuthBridgeModule.swift in the directory modules/caf-smart-auth-react-native/ios/. This file will contain the class that implements the native module.
Key Functions of the CafSmartAuthBridgeModule Class
build: Creates and configures an instance of the CafSmartAuthSdk using the provided parameters.
setupListener: Sets up a listener to monitor the status of authentication operations.
startSmartAuth: A method exposed to React Native to initiate smart authentication.
Example Implementation of the CafSmartAuthBridgeModule.swift File
import ExpoModulesCore
import CafSmartAuth
private struct CafSmartAuthBridgeConstants {
static let moduleName: String = "CafSmartAuthBridgeModule"
static let startSmartAuth: String = "startSmartAuth"
static let cafSmartAuthSuccessEvent: String = "CafSmartAuth_Success"
static let cafSmartAuthPendingEvent: String = "CafSmartAuth_Pending"
static let cafSmartAuthErrorEvent: String = "CafSmartAuth_Error"
static let cafSmartAuthCancelEvent: String = "CafSmartAuth_Cancel"
static let cafSmartAuthLoadingEvent: String = "CafSmartAuth_Loading"
static let cafSmartAuthLoadedEvent: String = "CafSmartAuth_Loaded"
static let isAuthorized: String = "isAuthorized"
static let attestation: String = "attestation"
static let errorMessage: String = "message"
static let isCancelled: String = "isCancelled"
static let isLoading: String = "isLoading"
static let isLoaded: String = "isLoaded"
static let cafFilterNaturalIndex: Int = 0
}
public class CafSmartAuthBridgeModule: Module {
// Each module class must implement the definition function. The definition consists of components
// that describes the module's functionality and behavior.
// See https://docs.expo.dev/modules/module-api for more details about available components.
private var smartAuth: CafSmartAuthSdk?
public func definition() -> ModuleDefinition {
// Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
// Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
// The module will be accessible from `requireNativeModule('CafSmartAuthBridgeModule')` in JavaScript.
Name(CafSmartAuthBridgeConstants.moduleName)
// Defines event names that the module can send to JavaScript.
Events(
CafSmartAuthBridgeConstants.cafSmartAuthSuccessEvent,
CafSmartAuthBridgeConstants.cafSmartAuthPendingEvent,
CafSmartAuthBridgeConstants.cafSmartAuthErrorEvent,
CafSmartAuthBridgeConstants.cafSmartAuthCancelEvent,
CafSmartAuthBridgeConstants.cafSmartAuthLoadingEvent,
CafSmartAuthBridgeConstants.cafSmartAuthLoadedEvent
)
// Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.
Function(CafSmartAuthBridgeConstants.startSmartAuth) { (mfaToken: String, faceAuthToken: String, personId: String, policyId: String, settings: String?) -> Void in
DispatchQueue.main.async {
self.smartAuth = self.build(
mfaToken: mfaToken, faceAuthToken: faceAuthToken, settings: CafSmartAuthBridgeSettings().parseJson(settings: settings)
)
self.smartAuth?.verifyPolicy(personID: personId, policyId: policyId, listener: self.setupListener())
}
}
}
private func build(
mfaToken: String,
faceAuthToken: String,
settings: CafSmartAuthBridgeSettingsModel?
) -> CafSmartAuthSdk {
let builder = CafSmartAuthSdk.CafBuilder(mobileToken: mfaToken)
if let stage = settings?.stage, let cafStage = CAFStage(rawValue: stage) {
_ = builder.setStage(cafStage)
}
let filter: CafFilterStyle = {
if let faceSettings = settings?.faceAuthenticationSettings, faceSettings.filter == CafSmartAuthBridgeConstants.cafFilterNaturalIndex {
return .natural
}
return .lineDrawing
}()
_ = builder.setLivenessSettings(
CafFaceLivenessSettings(
faceLivenessToken: faceAuthToken,
useLoadingScreen: settings?.faceAuthenticationSettings?.loadingScreen ?? false,
filter: filter
)
)
return builder.build()
}
private func setupListener() -> CafVerifyPolicyListener {
return { result in
switch result {
case .onSuccess(let response):
self.sendEvent(CafSmartAuthBridgeConstants.cafSmartAuthSuccessEvent, [
CafSmartAuthBridgeConstants.isAuthorized: response.isAuthorized,
CafSmartAuthBridgeConstants.attestation: response.attestation
])
self.smartAuth = nil
case .onPending(let response):
self.sendEvent(CafSmartAuthBridgeConstants.cafSmartAuthPendingEvent, [
CafSmartAuthBridgeConstants.isAuthorized: response.isAuthorized,
CafSmartAuthBridgeConstants.attestation: response.attestation
])
case .onError(let error):
self.sendEvent(CafSmartAuthBridgeConstants.cafSmartAuthErrorEvent, [
CafSmartAuthBridgeConstants.errorMessage: error.localizedDescription
])
self.smartAuth = nil
case .onCanceled(_):
self.sendEvent(CafSmartAuthBridgeConstants.cafSmartAuthCancelEvent, [
CafSmartAuthBridgeConstants.isCancelled: true
])
self.smartAuth = nil
case .onLoading:
self.sendEvent(CafSmartAuthBridgeConstants.cafSmartAuthLoadingEvent, [
CafSmartAuthBridgeConstants.isLoading: true
])
case .onLoaded:
self.sendEvent(CafSmartAuthBridgeConstants.cafSmartAuthLoadedEvent, [
CafSmartAuthBridgeConstants.isLoaded: true
])
}
}
}
}
3. Create the CafSmartAuthBridgeSettings.swift File
This file will handle the interpretation of data sent from React Native to the native module.
Example Implementation:
import CafSmartAuth
internal struct CafFaceAuthenticationSettingsModel: Decodable {
let loadingScreen: Bool?
let filter: Int?
}
internal struct CafSmartAuthBridgeSettingsModel: Decodable {
let stage: Int?
let faceAuthenticationSettings: CafFaceAuthenticationSettingsModel?
}
internal class CafSmartAuthBridgeSettings {
internal func parseJson(settings: String) -> CafSmartAuthBridgeSettingsModel? {
guard let data = settings.data(using: .utf8) else {
return nil
}
do {
let decoder = JSONDecoder()
let parsedSettings = try decoder.decode(CafSmartAuthBridgeSettingsModel.self, from: data)
return parsedSettings
} catch {
return nil
}
}
}