How to Create a Native Module in iOS
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 .
1. Open the iOS Project in Xcode
First, open the iOS project of your React Native application in Xcode. You can find the iOS project in the folder: YourApp/ios/your-app-name.xcworkspace
.
Using Xcode helps in quickly identifying issues, such as syntax errors, and provides powerful tools for iOS development.
2. Create the Native Module
Create a file named CafSmartAuthBridgeModule.swift
in the directory ios/
. This file will contain the class that implements the native module.
Key Functions of the CafSmartAuthBridgeModule
Class
requiresMainQueueSetup
: Indicates whether the module needs to be set up on the main queue. Returns true
.
supportedEvents
: Returns the list of events that the module can emit to React Native.
build
: Creates and configures an instance of the CafSmartAuthSdk
using the provided parameters.
emitEvent
: Emits events from the native side to React Native.
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
Copy import Foundation
import React
import CafSmartAuth
private struct CafSmartAuthBridgeConstants {
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 cafFilterNaturalIndex: Int = 0
static let backgroundColorHex: String = "#FFFFFF"
static let textColorHex: String = "#FF000000"
static let primaryColorHex: String = "#004AF7"
static let boxBackgroundColorHex: String = "#0A004AF7"
}
@objc(CafSmartAuthBridgeModule)
class CafSmartAuthBridgeModule: RCTEventEmitter {
private var smartAuth: CafSmartAuthSdk?
@objc
override static func requiresMainQueueSetup() -> Bool {
return true
}
override func supportedEvents() -> [String]! {
return [
CafSmartAuthBridgeConstants.cafSmartAuthSuccessEvent,
CafSmartAuthBridgeConstants.cafSmartAuthPendingEvent,
CafSmartAuthBridgeConstants.cafSmartAuthErrorEvent,
CafSmartAuthBridgeConstants.cafSmartAuthCancelEvent,
CafSmartAuthBridgeConstants.cafSmartAuthLoadingEvent,
CafSmartAuthBridgeConstants.cafSmartAuthLoadedEvent
]
}
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)
}
if let emailUrl = settings?.emailUrl {
_ = builder.setEmailURL(URL(string: emailUrl))
}
if let phoneUrl = settings?.phoneUrl {
_ = builder.setPhoneURL(URL(string: phoneUrl))
}
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
)
)
let lightTheme = settings?.theme?.lightTheme
let darkTheme = settings?.theme?.darkTheme
_ = builder.setThemeConfigurator(
CafThemeConfigurator(
lightTheme: parseTheme(theme: lightTheme),
darkTheme: parseTheme(theme: darkTheme)
)
)
return builder.build()
}
private func parseTheme(theme: CafSmartAuthBridgeTheme?) -> CafTheme {
if theme != nil {
return CafTheme(
backgroundColor: theme?.backgroundColor ?? CafSmartAuthBridgeConstants.backgroundColorHex,
textColor: theme?.textColor ?? CafSmartAuthBridgeConstants.textColorHex,
linkColor: theme?.linkColor ?? CafSmartAuthBridgeConstants.primaryColorHex,
boxBorderColor: theme?.boxBorderColor ?? CafSmartAuthBridgeConstants.primaryColorHex,
boxFilledBorderColor: theme?.boxFilledBorderColor ?? CafSmartAuthBridgeConstants.primaryColorHex,
boxBackgroundColor: theme?.boxBackgroundColor ?? CafSmartAuthBridgeConstants.boxBackgroundColorHex,
boxFilledBackgroundColor: theme?.boxFilledBackgroundColor ?? CafSmartAuthBridgeConstants.boxBackgroundColorHex,
boxTextColor: theme?.boxTextColor ?? CafSmartAuthBridgeConstants.primaryColorHex,
progressColor: theme?.progressColor ?? CafSmartAuthBridgeConstants.primaryColorHex
)
} else {
return CafTheme(
backgroundColor: CafSmartAuthBridgeConstants.backgroundColorHex,
textColor: CafSmartAuthBridgeConstants.textColorHex,
linkColor: CafSmartAuthBridgeConstants.primaryColorHex,
boxBorderColor: CafSmartAuthBridgeConstants.primaryColorHex,
boxFilledBorderColor: CafSmartAuthBridgeConstants.primaryColorHex,
boxBackgroundColor: CafSmartAuthBridgeConstants.boxBackgroundColorHex,
boxFilledBackgroundColor: CafSmartAuthBridgeConstants.boxBackgroundColorHex,
boxTextColor: CafSmartAuthBridgeConstants.primaryColorHex,
progressColor: CafSmartAuthBridgeConstants.primaryColorHex
)
}
}
private func emitEvent(name: String, data: Any) {
self.sendEvent(withName: name, body: data)
}
private func setupListener() -> CafVerifyPolicyListener {
return { result in
switch result {
case .onSuccess(let response):
self.emitEvent(
name: CafSmartAuthBridgeConstants.cafSmartAuthSuccessEvent,
data: [
CafSmartAuthBridgeConstants.isAuthorized: response.isAuthorized,
CafSmartAuthBridgeConstants.attestation: response.attestation
]
)
self.smartAuth = nil
case .onPending(let response):
self.emitEvent(
name: CafSmartAuthBridgeConstants.cafSmartAuthPendingEvent,
data: [
CafSmartAuthBridgeConstants.isAuthorized: response.isAuthorized,
CafSmartAuthBridgeConstants.attestation: response.attestation
]
)
case .onError(let error):
self.emitEvent(
name: CafSmartAuthBridgeConstants.cafSmartAuthErrorEvent,
data: [CafSmartAuthBridgeConstants.errorMessage: error.error.localizedDescription]
)
self.smartAuth = nil
case .onCanceled(_):
self.emitEvent(
name: CafSmartAuthBridgeConstants.cafSmartAuthCancelEvent,
data: true
)
self.smartAuth = nil
case .onLoading:
self.emitEvent(name: CafSmartAuthBridgeConstants.cafSmartAuthLoadingEvent, data: true)
case .onLoaded:
self.emitEvent(name: CafSmartAuthBridgeConstants.cafSmartAuthLoadedEvent, data: true)
}
}
}
@objc(startSmartAuth:livenessToken:personId:policyId:settings:)
func startSmartAuth(mfaToken: String, faceAuthToken: String, personId: String, policyId: String, settings: String?) {
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())
}
}
}
3. Create the CafSmartAuthBridgeSettings.swift
File
This file will handle the interpretation of data sent from React Native to the native module.
Example Implementation:
Copy import Foundation
import CafSmartAuth
internal struct CafFaceAuthenticationSettingsModel: Decodable {
let loadingScreen: Bool?
let filter: Int?
}
internal struct CafSmartAuthBridgeTheme: Decodable {
let backgroundColor: String?
let textColor: String?
let progressColor: String?
let linkColor: String?
let boxBackgroundColor: String?
let boxFilledBackgroundColor: String?
let boxBorderColor: String?
let boxFilledBorderColor: String?
let boxTextColor: String?
}
internal struct CafSmartAuthBridgeThemeConfigurator: Decodable {
let lightTheme: CafSmartAuthBridgeTheme?
let darkTheme: CafSmartAuthBridgeTheme?
}
internal struct CafSmartAuthBridgeSettingsModel: Decodable {
let stage: Int?
let faceAuthenticationSettings: CafFaceAuthenticationSettingsModel?
let emailUrl: String?
let phoneUrl: String?
let theme: CafSmartAuthBridgeThemeConfigurator?
}
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
}
}
}
4. Register the Native Module
Create the main header and implementation files for the custom native module. Create a new file named CafSmartAuthBridge.h
.
Example Implementation:
Copy #import <React/RCTBridgeModule.h>
@interface CafSmartAuthBridgeModule : NSObject <RCTBridgeModule>
@end
To make the native module recognizable by React Native, create the file CafSmartAuthBridge.mm
in the same directory:
Example Implementation:
Copy #import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RCT_EXTERN_MODULE(CafSmartAuthBridgeModule, RCTEventEmitter)
RCT_EXTERN_METHOD(startSmartAuth:(NSString *)mfaToken livenessToken:(NSString *)livenessToken personId:(NSString *)personId policyId:(NSString *)policyId settings:(NSString *)settings)
@end
5. Create the Bridging Header for Swift and Objective-C
Whenever you mix Swift and Objective-C in an iOS project, a bridging header file is required. This file allows Swift to access functionalities implemented in Objective-C, including React Native APIs.
In the same directory as the Swift file, create the file CafSmartAuthBridge-Bridging-Header.h
.
Copy #import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
Configuration Steps
Ensure the CafSmartAuthBridge-Bridging-Header.h
file is included in the Objective-C Bridging Header field in Build Settings (Build Settings > Swift Compiler - General > Objective-C Bridging Header
).
Provide the relative path to the file, e.g.,
Copy YourApp/ios/YourApp/CafSmartAuthBridge-Bridging-Header.h