📲 iOS, Android & React Native
The eKYC SDK ships native wrappers for iOS (Swift / Objective-C), Android (Kotlin / Java), and React Native. Each wrapper presents a full-screen WebView running the same production engine as the Web SDK from a hosted bridge page — so document auto-capture and Face Active Liveness behave identically on every platform, engine improvements reach your app without an update, and the wrappers add almost nothing to your binary size.
All wrappers expose the same three camera flows:
| Flow | What it does | API charged |
|---|---|---|
documentCapture | Auto-captures a document and submits it for OCR (ID card, passport, official cards) | 0.75–1.25 IC |
activeLiveness | Face Active Liveness challenges + server-signed verdict | 1 IC |
faceCapture | Auto-captures a sharp frontal selfie (no challenges) for Face Verification / Passive Liveness | free (capture only) |
Requirements: internet access to https://iapp.co.th/sdk/webview.html at runtime (eKYC needs connectivity for the API calls anyway), plus a camera.
iOS (Swift / Objective-C)
Install: Xcode → File → Add Package Dependencies… → https://github.com/iapp-technology/iapp-ekyc-sdk → product IappEkyc. Requires iOS 15+ and an NSCameraUsageDescription entry in Info.plist.
import IappEkyc
let config = IappEkycConfig(apiKey: "YOUR_API_KEY", flow: .documentCapture)
config.documentType = .thaiIdFront // .thaiIdBack .passport .thaiDriverLicense .bookBank .thaiIdWithSignature
config.locale = .th // .en / .th / .zh
IappEkycSdk.present(from: self, config: config) { result in
switch result {
case .success(let outcome):
print(outcome.document?.rawJSON ?? [:]) // full OCR response
case .failure(let error as NSError)
where error.code == IappEkycErrorCode.cancelled.rawValue:
break // user cancelled
case .failure(let error):
print(error.localizedDescription) // error.code: IappEkycErrorCode
}
}
Active liveness returns the server-signed verdict:
let config = IappEkycConfig(apiKey: "YOUR_API_KEY", flow: .activeLiveness)
IappEkycSdk.present(from: self, config: config) { result in
if case .success(let outcome) = result, let liveness = outcome.liveness {
// Verify verdictJSON + signature on YOUR backend — never trust
// `passed` alone on-device.
upload(liveness.verdictJSON, liveness.signature, liveness.selfieImageData)
}
}
Objective-C uses the same classes through a delegate:
@import IappEkyc;
IappEkycConfig *config = [[IappEkycConfig alloc] initWithApiKey:@"YOUR_API_KEY"
flow:IappEkycFlowTypeDocumentCapture];
config.documentType = IappEkycDocumentTypeThaiIdFront;
[IappEkycSdk presentFrom:self config:config delegate:self];
// Implement IappEkycViewControllerDelegate: didFinishWithResult / didFailWithError / didCancel
Android (Kotlin / Java)
Install via JitPack — minSdk 24, with an up-to-date Android System WebView:
// settings.gradle.kts
dependencyResolutionManagement {
repositories { google(); mavenCentral(); maven("https://jitpack.io") }
}
// app/build.gradle.kts
dependencies { implementation("com.github.iapp-technology:iapp-ekyc-sdk:v0.2.0") }
The library declares the CAMERA permission and requests it at runtime by itself.
val config = IappEkycConfig.Builder("YOUR_API_KEY").locale(EkycLocale.TH).build()
private val ekyc = registerForActivityResult(IappEkycContract()) { result ->
when (result) {
is IappEkycResult.DocumentCaptured -> handleOcr(result.rawJson)
is IappEkycResult.LivenessPassed ->
// Verify verdictJson + signature on YOUR backend.
upload(result.verdictJson, result.signature, result.selfieImage)
is IappEkycResult.FaceCaptured -> useSelfie(result.image)
is IappEkycResult.Failed -> show(result.error) // error.code: EkycErrorCode
IappEkycResult.Cancelled -> {}
}
}
// Launch any flow:
ekyc.launch(IappEkycRequest.DocumentCapture(config, EkycDocumentType.THAI_ID_FRONT))
ekyc.launch(IappEkycRequest.ActiveLiveness(config))
ekyc.launch(IappEkycRequest.FaceCapture(config))
Java is fully supported through the callback API:
IappEkyc.start(this,
new IappEkycRequest.DocumentCapture(config, EkycDocumentType.THAI_ID_FRONT),
new IappEkycCallback() {
@Override public void onResult(IappEkycResult result) { /* ... */ }
@Override public void onError(IappEkycError error) { /* ... */ }
@Override public void onCancelled() { }
});
React Native
Install (npm publication pending — install from a checkout) with the react-native-webview peer dependency:
git clone https://github.com/iapp-technology/iapp-ekyc-sdk
npm install ./iapp-ekyc-sdk/react-native react-native-webview
cd ios && pod install
iOS: add NSCameraUsageDescription. Android: add the CAMERA permission and request it before mounting the flow.
import { useState } from 'react';
import { Button, Modal, PermissionsAndroid, Platform } from 'react-native';
import { IappEkycFlow } from '@iapp-technology/react-native-ekyc-sdk';
function KycScreen() {
const [active, setActive] = useState(false);
const start = async () => {
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) return;
}
setActive(true);
};
return (
<>
<Button title="Capture Thai ID" onPress={start} />
<Modal visible={active} animationType="slide" presentationStyle="fullScreen">
<IappEkycFlow
flow="documentCapture" // 'documentCapture' | 'activeLiveness' | 'faceCapture'
documentType="thaiIdFront"
apiKey="YOUR_API_KEY"
locale="th"
onResult={(result) => { setActive(false); console.log(result); }}
onError={(error) => setActive(false)} // error.code, e.g. 'INSUFFICIENT_CREDIT'
onCancel={() => setActive(false)}
/>
</Modal>
</>
);
}
Shared behavior
- API key security — pass
apiKey: ""and setbaseUrlto your own backend to keep the key server-side (proxy mode). The key is injected into the bridge page after load, never through the URL. Details: SECURITY.md on GitHub. - Theming & locales — the same tokens and
en/th/zhlocales as the Web SDK, passed via each platform's config object. - Verdicts — active-liveness results are only proven by the server-signed verdict: recompute the HMAC on your backend before trusting
passed. See Face Active Liveness. - Cancellation — the in-flow Cancel button, the Android back button, and iOS swipe-dismiss all surface as a cancel outcome; dismissing/unmounting the flow aborts it cleanly.
- Bridge contract — the full wrapper↔engine protocol is documented in WEBVIEW_BRIDGE.md on GitHub.