iOS, Android & React Native
The native wrappers for iOS (Swift / Objective-C), Android (Kotlin / Java) and React Native open a full-screen WebView that runs the Web SDK engine from a hosted page, so capture behaves the same on every platform and engine updates reach your app without a release. At runtime they need a camera and internet access to https://iapp.co.th/sdk/webview.html.
Every wrapper has the same flows:
documentCapturecaptures a document and submits it for OCR (ID card, passport, official cards). 0.75–1.25 IC.activeLivenessruns the Face Active Liveness challenges and returns a server-signed verdict. 1 IC.faceCapturecaptures a sharp frontal selfie, without challenges, for Face Verification or Passive Liveness. Free, capture only.
Install and run a flow
- iOS
- Android
- React Native
- Requires iOS 15+ and
NSCameraUsageDescriptioninInfo.plist. - In Xcode choose File → Add Package Dependencies…, enter
https://github.com/iapp-technology/iapp-ekyc-sdkand add the product IappEkyc. - Present a flow and handle the result.
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
- Requires minSdk 24 and an up-to-date Android System WebView (Chrome/WebView ≥ 100 recommended).
- Add JitPack and the dependency.
- Register the contract and launch any flow. The library declares the
CAMERApermission and requests it at runtime itself.
// 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") }
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 uses 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() { }
});
- Requires React Native ≥ 0.72 and
react-native-webview≥ 13.6. The npm package is not published yet, so install from a checkout. - iOS: add
NSCameraUsageDescription. Android: add theCAMERApermission and request it before mounting the flow. - Render
IappEkycFlowin a full-screen Modal.
git clone https://github.com/iapp-technology/iapp-ekyc-sdk
npm install ./iapp-ekyc-sdk/react-native react-native-webview
cd ios && pod install
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: pass
apiKey: ""and setbaseUrlto your own backend to keep the key on the server. The key reaches the bridge page after it loads, never through the URL. SECURITY.md - Theme and language: the same tokens and
en/th/zhlocales as the Web SDK, set in each platform's config object. - Liveness verdicts: only the server-signed verdict proves a pass. Recompute its HMAC on your backend before trusting
passed; see Face Active Liveness. - Cancel: the in-flow Cancel button, the Android back button and an iOS swipe-dismiss all return a cancel outcome; dismissing or unmounting the flow aborts it cleanly.
- Bridge protocol: the full wrapper-to-engine contract is in WEBVIEW_BRIDGE.md.