@valifysolutions/crossplatform-capacitor-vidvdockit
v1.0.1
Published
Capacitor plugin for the VIDV DocKit identity verification and document capture SDK (Android & iOS).
Readme
@valifysolutions/crossplatform-capacitor-vidvdockit
Capacitor plugin for the VIDV DocKit identity verification and document capture SDK, on Android and iOS.
This plugin is a thin bridge — all document capture, OCR, liveness, and NFC logic lives in the native SDKs. The plugin only:
- Maps a JS
DocKitConfigonto the native builder (VIDVDocKitConfig.Builderon Android,VIDVDocBuilderon iOS). - Starts the native SDK's own UI flow (an Activity on Android, a modally-presented flow on iOS).
- Maps the native result callback back into a resolved Capacitor call.
JS/TS → DocKit.start(config)
│
┌───────────────────────┴───────────────────────┐
▼ ▼
DocKitPlugin (Android) ──build──▶ VIDVDocKitConfig.Builder DocKitPlugin (iOS) ──build──▶ VIDVDocBuilder
│ │ │ │
│ builder.start(activity, listener)│ builder.start(viewController, delegate)
│ ▼ │ ▼
│ VidvDocKitHostActivity (SDK UI) │ SDK presents its own flow modally
│ │ │ │
│ VIDVDocKitListener.onDocKitResult(response) │ VIDVDocKitDelegate.onDocKitResult(response)
◀────────────────────────────────────┘ ◀────────────────────────────────┘
Promise<DocKitResponse> Promise<DocKitResponse>Your app's MainActivity/AppDelegate need no changes — Capacitor's plugin autolinking (npx cap sync)
discovers and registers DocKitPlugin automatically from this package's capacitor.android.src /
capacitor.ios.src fields, the same way every other Capacitor plugin does.
Install
npm install @valifysolutions/crossplatform-capacitor-vidvdockit
npx cap syncAndroid
The native SDK is resolved from Valify's private Artifactory repo, which this plugin's android/build.gradle
already registers. If you have your own Artifactory credentials, override the defaults in your app's
android/gradle.properties:
valifyArtifactoryUser=your-user
valifyArtifactoryPassword=your-passwordYour app must declare the INTERNET permission (Capacitor apps already do). Camera, location (only if
collectUserInfo is enabled) and NFC permissions/manifest entries come from the SDK's own AAR and are
requested at runtime by the SDK's own Activity — no action needed in your app.
iOS
The native VIDVDocKit XCFramework (currently v1.7.1) is vendored automatically by this plugin's own
ValifysolutionsCrossplatformCapacitorVidvdockit.podspec (CocoaPods) — no extra Podfile entry needed beyond the usual pod install
that npx cap sync ios already runs for you. CocoaPods is the only supported iOS integration path — this
plugin has no Package.swift/Swift Package Manager manifest, so apps using SPM for their own dependencies
must still integrate this plugin (and VIDVDocKit) via CocoaPods.
Add the permission usage strings the native SDK needs to your app's Info.plist (these mirror the original
iOS SDK Demo's own Info.plist):
<key>NSCameraUsageDescription</key>
<string>We need access to your camera to scan documents.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to provide relevant features and improve your app experience.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need access to your location even when the app is in the background to support continuous services.</string>
<key>NFCReaderUsageDescription</key>
<string>This app reads your biometric passport chip</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
</array>(NSLocation* and NFCReaderUsageDescription/the NFC AID entry are only exercised when collectUserInfo
is enabled or documentType is ePassport, respectively — but iOS requires the usage strings to be present
whenever the SDK binary links the relevant frameworks, regardless of whether your own config path uses
them.)
Your app also needs the Near Field Communication Tag Reading capability (adds
com.apple.developer.nfc.readersession.formats: [TAG] to your entitlements) if you use documentType:
'ePassport'.
iOS Simulator is not supported. The published VIDVDocKit.xcframework currently ships a device-only
(arm64) slice with no Simulator slice at all — this plugin's podspec excludes simulator architectures so a
Simulator build of your app doesn't hard-fail at link time, but the DocKit flow itself only runs on a
physical device.
Usage
import { DocKit } from '@valifysolutions/crossplatform-capacitor-vidvdockit';
const response = await DocKit.start({
baseUrl: 'https://api.valifystage.com',
accessToken: myAccessToken,
bundleKey: myBundleKey,
documentType: 'egyNID',
language: 'en',
reviewData: true,
captureMode: 'automatic',
extras: ['advanced_confidence', 'document_liveness'],
});
switch (response.state) {
case 'SUCCESS':
console.log('Captures:', response.result?.captures);
console.log('Extracted data:', response.result?.extractedData);
break;
case 'FAILURE':
console.warn(`DocKit failed (${response.code}): ${response.message}`);
break;
case 'EXIT':
console.log(`User exited at step: ${response.step}`);
break;
case 'ERROR':
console.error(`Invalid configuration (${response.code}): ${response.message}`);
break;
}DocKit.start() only rejects for conditions outside the SDK's own result model — no foreground
Activity on Android (NO_ACTIVITY), no presentable view controller on iOS (NO_VIEW_CONTROLLER), or a
config-building failure such as an invalid primaryColor hex string (INIT_ERROR). Every SDK outcome,
including user cancellation and validation/service failures, resolves the promise with the
corresponding state — see "Android/iOS differences" below for the one exception
(missing/invalid documentType).
API
start(...)
start(options: DocKitConfig) => Promise<DocKitResponse>Launches the VIDV DocKit SDK with the given configuration and resolves with the session outcome once the SDK's own UI finishes.
| Param | Type |
| ------------- | ---------------------------------------------------- |
| options | DocKitConfig |
Returns: Promise<DocKitResponse>
Interfaces
DocKitConfig
Configuration for a DocKit session. Field names are camelCase (idiomatic Capacitor/TypeScript style); each
is mapped 1:1 to a setter on the native builder — VIDVDocKitConfig.Builder on Android, VIDVDocBuilder on
iOS — except where noted as platform-specific.
| Field | Type | Required | Default | Android setter | iOS setter |
| ---------------------------- | -------------------------------------------------------------------------------- | :------: | ------------- | ---------------------------------- | ------------------------------------ |
| baseUrl | string | ✅ | — | setBaseUrl | setBaseURL |
| accessToken | string | ✅ | — | setAccessToken | setAccessToken |
| bundleKey | string | ✅ | — | setBundleKey | setBundleKey |
| documentType | DocumentType | ✅ | — | setDocumentType | setDocumentType |
| language | Language | | en | setLanguage | setLanguage |
| reviewData | boolean | | true | setReviewData | setReviewData |
| collectUserInfo | boolean | | false | setCollectUserInfo | setCollectUserInfo |
| previewCapturedImage | boolean | | false | previewCapturedImage | setPreviewCapturedImage |
| captureMode | CaptureMode | | automatic | setCaptureMode | setCaptureMode |
| captureModeSeconds | number | | 10 | VIDVCaptureMode.AUTO_AFTER(secs) | VIDVCaptureMode.manualAfter(secs) |
| captureOnlyMode | boolean | | false | setCaptureOnlyMode | setCaptureOnlyMode |
| epassportSecurityConfig | boolean | | true | setEPassportSecurityConfig | ignored — no iOS equivalent as of SDK 1.7.1 |
| manualCaptureValidation | boolean | | false | manualCaptureValidation | ignored — no iOS equivalent |
| primaryColor | string (hex, e.g. "#62CBC9") | | native default | setPrimaryColor | setPrimaryColor |
| headers | Record<string, string> | | {} | setHeaders | setHeaders |
| sslCertificate | string (base64) | | — | setSSLCertificate | setSSLCertificate |
| customLogo | string (base64) | | — | setLogo(ByteArrayLogo(...)) | setLogo(.customLogo(...)) |
| disableLogo | boolean | | false | setLogo(Empty()) | setLogo(.empty) |
| customFontResourceName | string (Android font resource name, e.g. "custom_font") | | — | setCustomFont(resId) | ignored — see customFontName |
| customFontName | string (iOS font/family name, e.g. "HelveticaNeue-Bold") | | — | ignored | setCustomFont(UIFont(name:size:)) |
| customFontSize | number | | 16 | ignored | (used with customFontName) |
| extras | ExtrasConfig | | [] | setExtras | setExtras |
| documentValidation | DocumentValidationRuleConfig[] | | | documentValidation | ignored — no iOS equivalent |
customFontResourceName(Android) andcustomFontName/customFontSize(iOS) can't ship a font file on your behalf the waycustomLogo/sslCertificatedo — both platforms require the font to already be bundled/registered by your own app. On Android, bundle a.ttf(or font-family XML) underandroid/app/src/main/res/font/and pass its resource name (without extension). On iOS, register the font (e.g. viaUIAppFontsinInfo.plist, orCTFontManagerRegisterFontsForURL) and pass its PostScript/family name. A name that doesn't resolve falls back to the SDK's default font on Android; on iOS it failsDocKit.start()withINIT_ERRORinstead, since there's no silent "ignore and use default" case to fall back to at the point the font would be constructed.
DocKitResponse
| Field | Type | Set for |
| ------------ | ------------------------------------------------------------ | ------------------------- |
| state | DocKitState | always |
| code | number | FAILURE, ERROR |
| message | string | FAILURE, ERROR |
| step | string | EXIT |
| result | DocKitResultData | SUCCESS, and often FAILURE/EXIT |
DocKitResultData
Mirrors me.vidv.vidvdockitsdk.global.VIDVDocKitData on Android and VIDVDocumentKitResult on iOS.
| Field | Type | Platform |
| -------------------- | ----------------------------------------------- | --------------------- |
| sessionID | string | both |
| deviceID | string | Android only |
| captures | Record<string, string \| null> (base64 JPEG) | both* |
| extractedData | Record<string, unknown> | both |
| validationFailures | DocumentValidationFailure[] | Android only |
| validations | NFCValidations | iOS only |
* Same type on both platforms, but not the same presence guarantee — see "Android/iOS differences" below.
NFCValidations
iOS only. NFC passive authentication / MRZ cross-check / face match detail, populated for ePassport
sessions. Mirrors the native VIDVNFCValidations struct.
| Field | Type |
| ----------------------- | ---------------------------------------------------------- |
| passiveAuthentication | { status?: boolean; reason?: string } |
| mrzCrossCheck | { status?: string; mismatchedFields?: string[] } |
| faceMatchValidation | { status?: boolean } |
DocumentValidationRuleConfig
| Field | Type | Notes |
| --------------- | ---------------------------------------------------------------------- | --------------------------------------------- |
| path | string | Dot-notation path into extractedData |
| op | DocumentValidationOperator | |
| value | string \| number \| boolean | Not used for IS_TRUE/IS_FALSE/IS_EMPTY/IS_NOT_EMPTY/MATCH |
| matchPath | string | For MATCH: path to compare against instead of value |
| ignoreValues | string[] | For IS_EMPTY: extra values treated as empty |
Type Aliases
DocumentType
'passport' | 'ePassport' | 'egyNID' | 'tunNID' | 'dzaNID'
CaptureMode
'automatic' | 'manual' | 'autoAfter'
Language
'en' | 'ar' | 'fr'
ExtrasConfig
string[] | Record<string, boolean>
DocKitState
'SUCCESS' | 'FAILURE' | 'EXIT' | 'ERROR'
DocumentValidationOperator
'EQUAL' | 'NOT_EQUAL' | 'GREATER_THAN' | 'GREATER_THAN_OR_EQUAL' | 'LESS_THAN' | 'LESS_THAN_OR_EQUAL' | 'IS_TRUE' | 'IS_FALSE' | 'IS_EMPTY' | 'IS_NOT_EMPTY' | 'CONTAINS' | 'NOT_CONTAINS' | 'MATCH'
Design notes / deviations from the React Native plugin
The React Native DocKit plugin was used as the reference for this plugin's architecture and API surface, and the native Android DocKit SDK as the source of truth for all native behavior. A few things are deliberately different from the RN plugin:
- camelCase field names (
baseUrl,documentType,captureMode, …) instead of RN's snake_case — idiomatic for Capacitor/TypeScript. The underlying native calls are identical. captureMode: 'automatic' | 'manual' | 'autoAfter'— the native SDK'sVIDVCaptureModesealed class has noMANUAL_AFTERvariant; RN accepted'manual_after'as an alias that silently mapped toAUTO_AFTER. This plugin exposes a single canonical'autoAfter'name instead.- No deprecated
manualCaptureboolean — RN kept it for its own backward compatibility. This is a new plugin with no existing consumers, and the SDK itself deprecates that flag in favor ofcaptureMode. - No
validations/NFCValidationsfield in the result — that field is iOS-only in RN (populated from ePassport NFC checks on iOS). The AndroidVIDVDocKitDatamodel has no such field, so it is correctly omitted here rather than invented.
Everything else — every config option, every result field, every error code (7000-7018), and the resolve-vs-reject contract — is preserved exactly as the native Android SDK and the RN plugin define it.
Android/iOS differences
The native iOS DocKit SDK (distributed as the
VIDVDocKit.xcframework, and its own Demo app) is the source of truth for all iOS behavior — it is not
assumed to work like the Android SDK. Every difference below comes from a real difference between the two
native SDKs, not from a choice made in this plugin:
documentValidationandmanualCaptureValidationhave no equivalent in the native iOS SDK's public API. Both are accepted inDocKitConfigfor shape parity and silently ignored on iOS —result.validationFailureswill never be populated there.result.validations(NFCValidations) is iOS-only, populated from the nativeVIDVDocumentKitResult. The Android SDK's result model has no such field.result.deviceIDis Android-only — the iOS result model has no such field.result.capturespresence — on Android, every expected capture key is always present, withnullfor a side that wasn't captured (e.g.{ front: "...", back: null }). On iOS, the nativeVIDVDocumentKitResult.capturesonly includes keys for sides that were actually captured, so an uncaptured side is missing from the object entirely rather than set tonull(e.g.{ front: "..." }, with nobackkey at all). Check for absence (captures.back == null) rather thancaptures.back === nullif you need identical behavior on both platforms.customFontResourceName(Android) vs.customFontName/customFontSize(iOS) — see theDocKitConfigtable above.epassportSecurityConfigis Android only. As of native iOS SDK1.7.1,VIDVDocBuilderno longer exposes a setter for ePassport NFC security checks (BAC/PACE/passive authentication) — the SDK handles them internally and they are not configurable from outside it. The option is accepted forDocKitConfigshape parity and silently ignored on iOS.- Missing/invalid
documentTyperejects withINIT_ERRORon Android (reading an unassigned Kotlinlateinitthrows before the SDK's own Activity ever starts), but resolvesstate: 'ERROR'(code7018) on iOS, whereVIDVBuilderValidatorcatches it and reports it through the normal delegate/result path. This plugin does not paper over that difference — seeDocKit.start()'s doc comment. - Invalid
language— Android's native SDK silently falls back to English for an unrecognized value; the iOS SDK's own validator rejects it asstate: 'ERROR'(code7018) instead. (Both platforms' publicLanguagetype only allows'en' | 'ar' | 'fr', so this only matters for direct/dynamic JS callers that bypass the type.) - No Simulator support on iOS — the published XCFramework ships a device-only slice today.
Development
npm install
npm run build # tsc + rollup, both platforms share the same dist output
npm run verify:android # ./gradlew clean build testThe example/ app demonstrates both platforms from the same JS/TS code — npx cap sync inside example/
after npm run build in the plugin root picks up local changes.
