Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,16 @@ jobs:
- name: Build example for iOS
run: |
yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"

- name: Test iOS bridge
if: env.turbo_cache_hit != 1
working-directory: example/ios
run: |
set -o pipefail
SIMULATOR_ID=$(xcrun simctl list devices available | grep -E '^[[:space:]]+iPhone' | grep -oE '[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}' | tail -n 1)
xcodebuild test \
-workspace SdkReactNativeExample.xcworkspace \
-scheme SdkReactNativeExample \
-configuration Debug \
-destination "id=$SIMULATOR_ID" \
-only-testing:SdkReactNativeExampleTests | xcbeautify
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ Remember to add tests for your change if possible. Run the unit tests by:
yarn test
```

The iOS bridge has XCTests in `example/ios/SdkReactNativeExampleTests`, hosted by the example app. CI runs them in the iOS job; on a Mac, run them after `bundle exec pod install` in `example/ios`:

```sh
cd example/ios
xcodebuild test \
-workspace SdkReactNativeExample.xcworkspace \
-scheme SdkReactNativeExample \
-destination 'platform=iOS Simulator,name=<any iPhone simulator>'
```


### Commit message convention

Expand Down
82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ A React Native wrapper for the Didit Identity Verification SDK. Supports both iO

## Permissions

The SDK uses the camera, location, and optionally NFC on both platforms. Native permissions are declared by the underlying native SDKs and merged automatically where the platform supports it.
The SDK uses the camera, optionally NFC, and location only for the Location verification step (see [Bank and Location verification steps](#bank-and-location-verification-steps)). Native permissions are declared by the underlying native SDKs and merged automatically where the platform supports it.

### iOS

Expand Down Expand Up @@ -85,6 +85,85 @@ The SDK handles Android runtime permission requests automatically. When the user

You do not need to request camera permission in your app code before calling `startVerification()` — the SDK manages this internally.

## Bank and Location verification steps

Workflows can include a **Bank** step (the person links a bank account) and a **Location** step (the device position is read once, with the person's consent). Both screens are drawn by the native SDKs, so this package needs no new API: they run inside `startVerification()` / `startVerificationWithWorkflow()`, and the outcome comes back as the same `VerificationResult`.

> **Availability:** native SDK releases up to and including 4.9.1 do not include these steps. Check the native versions this package pins (`diditNativeSdkVersions` in `package.json`) and keep the steps out of the workflows your app runs until it pins a native SDK release that has them.

### Bank

No setup. The SDK opens the bank's approval page in the system browser (a Custom Tab on Android, Safari in a sheet on iOS) and checks the result itself when the person comes back, so there is no permission, URL scheme or deep link to register.

### Location on iOS

Location lives in the native SDK's optional `DiditSDK/Location` module, so apps that never run the step never link Core Location. The released iOS binaries do not include that module yet; without it, or without `NSLocationWhenInUseUsageDescription`, the step shows no prompt, reports that the device cannot place the person, and the workflow's own rules decide.

Add the purpose strings to `Info.plist`. `NSLocationTemporaryUsageDescriptionDictionary` is optional: it lets the step ask once for precise location when the person granted only an approximate one (iOS 14+), and its key must be exactly `DiditLocationVerification`.

```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location is used to confirm where you are for this verification.</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>DiditLocationVerification</key>
<string>This verification needs your precise location once.</string>
</dict>
```

iOS picks these strings by the device language, not by the SDK's `languageCode`, so translate them in each `<lang>.lproj/InfoPlist.strings`. The precise-location string is translated under its purpose key. For example `es.lproj/InfoPlist.strings`:

```
"NSLocationWhenInUseUsageDescription" = "Tu ubicación se usa para confirmar dónde estás en esta verificación.";
"DiditLocationVerification" = "Esta verificación necesita tu ubicación precisa una vez.";
```

With Expo, set the same keys in `app.json` and translate them with [`locales`](https://docs.expo.dev/guides/localization/#translating-app-metadata):

```json
{
"expo": {
"ios": {
"infoPlist": {
"NSLocationWhenInUseUsageDescription": "Your location is used to confirm where you are for this verification.",
"NSLocationTemporaryUsageDescriptionDictionary": {
"DiditLocationVerification": "This verification needs your precise location once."
}
}
},
"locales": {
"es": "./locales/es.json"
}
}
}
```

where `locales/es.json` is:

```json
{
"ios": {
"NSLocationWhenInUseUsageDescription": "Tu ubicación se usa para confirmar dónde estás en esta verificación.",
"DiditLocationVerification": "Esta verificación necesita tu ubicación precisa una vez."
}
}
```

Both example apps in this repository declare these strings in English and Spanish.

Once your app links the module, declare **Precise Location** and **Coarse Location** in App Store Connect under **App Functionality**, linked to the user and not used for tracking. The SDK reads one position per attempt, only while the step is on screen, and never in the background.

### Location on Android

The native SDK declares `ACCESS_FINE_LOCATION` and `ACCESS_COARSE_LOCATION`, plus the location hardware features as optional (`android:required="false"`), and they merge into your app automatically. It asks for them at runtime only when a workflow reaches the step, after a screen that explains why, and the person can grant precise or approximate location. If your app never runs the step, you can remove them from the merged manifest (the `<manifest>` element needs `xmlns:tools="http://schemas.android.com/tools"`):

```xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove" />
```

A session that still reaches the step then reports the location as unavailable, without a prompt. If you keep them, declare **Approximate location** and **Precise location** as collected data in your Google Play Data safety form.

## Native SDK Variants

NFC and auto-detection are enabled by default on both platforms.
Expand Down Expand Up @@ -526,6 +605,7 @@ Both `startVerification` and `startVerificationWithWorkflow` return a `Promise<V
| `cameraAccessDenied` | Camera permission not granted |
| `notInitialized` | SDK not initialized (Android only) |
| `apiError` | API request failed |
| `retryBlocked` | A previous verification was not approved and the maximum number of retries was reached |
| `unknown` | Other error with message |

### Handling Results
Expand Down
7 changes: 7 additions & 0 deletions example-expo/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
"NSMicrophoneUsageDescription": "Microphone access is required to record video for liveness verification.",
"NSPhotoLibraryUsageDescription": "Photo library access is required to upload document images.",
"NFCReaderUsageDescription": "NFC is used to read passport chip data for identity verification.",
"NSLocationWhenInUseUsageDescription": "Your location is used to confirm where you are for this verification.",
"NSLocationTemporaryUsageDescriptionDictionary": {
"DiditLocationVerification": "This verification needs your precise location once."
},
"com.apple.developer.nfc.readersession.iso7816.select-identifiers": [
"D23300000045737445494420763335",
"A0000002471001",
Expand All @@ -30,6 +34,9 @@
]
}
},
"locales": {
"es": "./locales/es.json"
},
"android": {
"package": "me.didit.sdk.reactnative.expoexample",
"adaptiveIcon": {
Expand Down
13 changes: 13 additions & 0 deletions example-expo/ios/DiditExpoExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
D3B2F5EDC02B66C5935838A5 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92FAC627B51295BC573C26A /* ExpoModulesProvider.swift */; };
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
08781A61616F403BA05CE643 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9602DDAD615B4729B9401021 /* InfoPlist.strings */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -30,6 +31,7 @@
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = DiditExpoExample/AppDelegate.swift; sourceTree = "<group>"; };
F11748442D0722820044C1D9 /* DiditExpoExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "DiditExpoExample-Bridging-Header.h"; path = "DiditExpoExample/DiditExpoExample-Bridging-Header.h"; sourceTree = "<group>"; };
9602DDAD615B4729B9401021 /* InfoPlist.strings */ = {isa = PBXFileReference; name = "InfoPlist.strings"; path = "es.lproj/InfoPlist.strings"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = text.plist.strings; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -127,11 +129,21 @@
isa = PBXGroup;
children = (
BB2F792C24A3F905000567C9 /* Expo.plist */,
BBEEB4F65AD04F14A9D6BBC7 /* es.lproj */,
);
name = Supporting;
path = DiditExpoExample/Supporting;
sourceTree = "<group>";
};
BBEEB4F65AD04F14A9D6BBC7 /* es.lproj */ = {
isa = "PBXGroup";
children = (
9602DDAD615B4729B9401021 /* InfoPlist.strings */,
);
name = es.lproj;
sourceTree = "<group>";
path = "";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -197,6 +209,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
36DE53FAFBF2A3780CD20F58 /* PrivacyInfo.xcprivacy in Resources */,
08781A61616F403BA05CE643 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
7 changes: 7 additions & 0 deletions example-expo/ios/DiditExpoExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
</dict>
<key>NSCameraUsageDescription</key>
<string>Camera access is required for identity verification to capture documents and facial photos.</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>DiditLocationVerification</key>
<string>This verification needs your precise location once.</string>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location is used to confirm where you are for this verification.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required to record video for liveness verification.</string>
<key>NSPhotoLibraryUsageDescription</key>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NSLocationWhenInUseUsageDescription = "Tu ubicación se usa para confirmar dónde estás en esta verificación.";
DiditLocationVerification = "Esta verificación necesita tu ubicación precisa una vez.";
6 changes: 6 additions & 0 deletions example-expo/locales/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ios": {
"NSLocationWhenInUseUsageDescription": "Tu ubicación se usa para confirmar dónde estás en esta verificación.",
"DiditLocationVerification": "Esta verificación necesita tu ubicación precisa una vez."
}
}
5 changes: 5 additions & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ target 'SdkReactNativeExample' do
:app_path => "#{Pod::Config.instance.installation_root}/.."
)

# The bridge's XCTests run inside the example app, which already links the pods.
target 'SdkReactNativeExampleTests' do
inherit! :search_paths
end

post_install do |installer|
react_native_post_install(
installer,
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2806,6 +2806,6 @@ SPEC CHECKSUMS:
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: 6ca93c8c13f56baeec55eb608577619b17a4d64e

PODFILE CHECKSUM: 24e175dec25ff17e5b9150dbd85e30c04dc87b94
PODFILE CHECKSUM: 1175cc68b3547f6c408d7970385f57bff918c1ab

COCOAPODS: 1.16.2
Loading
Loading