diff --git a/README.md b/README.md index d40c378..40a27d6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ It is private and unavailable until the native DiditAuth integration is ready. | Platform | Minimum Version | Notes | |----------|----------------|-------| | iOS | 13.0+ | NFC passport reading requires iOS 15.0+ | -| Android | API 24+ (7.0) | Kotlin 1.9+, Java 17+ | +| Android | API 24+ (7.0) | Kotlin 2.1.10+, Android Gradle Plugin 8.6.0+, Gradle 8.7+, JDK 17 | ## Permissions @@ -93,17 +93,15 @@ You do not need to request camera permission in your app code before calling `st 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`. -> **Development preview:** these steps currently use the native integration source pinned in `diditNativeSdkVersions.source` in `package.json`. Set `DIDIT_SDK_SOURCE_PATH` to a clean checkout at that revision before building either example. A missing or different checkout fails the build instead of substituting the 4.9.1 release, which does not contain these steps. The temporary source pin must move to the released native version before the stacked push and package release. - -For Android source builds, apply this package's `android/native-source.gradle` from the host's `settings.gradle`. First run `yarn native:android` to build the pinned native revision with its own Gradle wrapper. The settings script exclusively resolves Didit artifacts from that local Maven repository, preventing a fallback to released artifacts. Both examples already do this; Expo prebuild adds it automatically. - ### 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. +No setup. The native SDKs open the bank's page (or a page Didit hosts) in the system browser, 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, which is not included in the `all` variant. Use CocoaPods with `$DiditSdkIosLocationEnabled = true` before `use_native_modules!`, and declare both the selected variant and `DiditSDK/Location` with the same source path. The bare example's Podfile shows this setup. Expo users set `iosLocationEnabled: true` in this package's plugin options and run prebuild. SwiftPM linkage currently cannot opt into Location. Without the module, or without `NSLocationWhenInUseUsageDescription`, the step shows no prompt, reports that the device cannot place the person, and the workflow's own rules decide. +Location ships in the native SDK as complete **Location variants**, not as an add-on: `Core-Location`, `NFC-Location` and `All-Location` for CocoaPods, `DiditSDKCoreLocation`, `DiditSDKNFCLocation` and `DiditSDKLocation` (all with Location) for SwiftPM. Exactly one DiditSDK variant may be declared, so this package picks it for you from your variant: `all` maps to `All-Location`, `core` and `autodetection` map to `Core-Location`, `nfc` maps to `NFC-Location`. + +Set `$DiditSdkIosLocationEnabled = true` before `use_native_modules!` (or `DIDIT_SDK_IOS_LOCATION_ENABLED=true`) and keep a single `pod didit_sdk_subspec` line as shown in [React Native CLI](#ios), with the Location mapping. With SwiftPM linkage the same flag selects the Location product and needs no pod line. Expo users set `iosLocationEnabled: true` in this package's plugin options and run prebuild. Without Location enabled, 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`. @@ -175,29 +173,18 @@ A session that still reaches the step then reports the location as unavailable, ## Native SDK Variants -NFC and auto-detection are enabled by default on both platforms. +NFC is enabled by default on both platforms. Automatic capture is available in every variant. The React Native plugin can install the same native SDK variants exposed by the iOS and Android SDKs: -| Variant | Automatic capture | NFC passport reading | Use when | -|---------|-------------------|----------------------|----------| -| `all` | Yes | Yes | You want the complete SDK (default) | -| `core` | No | No | You only need manual capture and the smallest binary | -| `autodetection` | Yes | No | You need automatic capture without NFC | -| `nfc` | No | Yes | You need NFC passport reading without automatic capture | - -### Empirical iOS Sizes +| Variant | NFC passport reading | Use when | +|---------|----------------------|----------| +| `all` | Yes | You want the complete SDK (default) | +| `core` | No | You do not need NFC passport reading | +| `nfc` | Yes | You want the NFC variant | +| `autodetection` | No | Deprecated: identical to `core` | -Measured against the React Native example app using a Release `iphoneos` build with native iOS SDK 4.0.2. `DiditSDK` is linked statically into the app executable, so the app executable is the most useful place to see the SDK's impact. Compressed values are IPA-like zips of the `.app` payload. - -| Variant | `.app` bundle | App executable Mach-O | OpenSSL framework | Compressed IPA-like zip | -|---------|--------------:|----------------------:|------------------:|------------------------:| -| `core` | 30.3 MB | 22.1 MB | - | 9.1 MB | -| `autodetection` | 49.4 MB | 41.1 MB | - | 15.2 MB | -| `nfc` | 41.3 MB | 23.1 MB | 9.8 MB | 14.2 MB | -| `all` | 60.3 MB | 42.2 MB | 9.8 MB | 20.3 MB | - -When NFC is disabled, you do not need iOS NFC capabilities, NFC entitlements, Android NFC packaging rules, or NFC-related provisioning setup. When auto-detection is disabled, the document and face capture steps fall back to a manual shutter button. +When NFC is disabled, you do not need iOS NFC capabilities, NFC entitlements, Android NFC packaging rules, or NFC-related provisioning setup. ## Installation @@ -249,7 +236,7 @@ The iOS podspec URL is derived from the native SDK version this package pins (`d [ "@didit-protocol/sdk-react-native", { - "iosPodspecUrl": "https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.9.1/DiditSDK.podspec" + "iosPodspecUrl": "https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.10.0/DiditSDK.podspec" } ] ``` @@ -269,7 +256,7 @@ yarn add @didit-protocol/sdk-react-native Add the DiditSDK pod to your `Podfile` (it's not on CocoaPods trunk). The default native variant is `all`: ```ruby -# Pick one: 'all', 'core', 'autodetection', 'nfc' +# Pick one: 'all', 'core', 'nfc' ('autodetection' is deprecated and identical to 'core') $DiditSdkIosVariant = 'all' def max_ios_version(*versions) @@ -280,15 +267,17 @@ didit_sdk_ios_deployment_target = ['all', 'nfc'].include?($DiditSdkIosVariant) ? platform :ios, didit_sdk_ios_deployment_target # Inside your app target: +# Location ships as complete variants (All-Location, Core-Location, NFC-Location) +$DiditSdkIosLocationEnabled = false # true to enable the Location step +didit_sdk_location_suffix = $DiditSdkIosLocationEnabled ? '-Location' : '' didit_sdk_subspec = case $DiditSdkIosVariant - when 'all' then 'DiditSDK/All' - when 'core' then 'DiditSDK/Core' - when 'autodetection' then 'DiditSDK/AutoDetection' - when 'nfc' then 'DiditSDK/NFC' + when 'all' then "DiditSDK/All#{didit_sdk_location_suffix}" + when 'core', 'autodetection' then "DiditSDK/Core#{didit_sdk_location_suffix}" + when 'nfc' then "DiditSDK/NFC#{didit_sdk_location_suffix}" else raise "Invalid $DiditSdkIosVariant '#{$DiditSdkIosVariant}'. Supported values: all, core, autodetection, nfc." end -pod didit_sdk_subspec, :podspec => 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.9.1/DiditSDK.podspec' +pod didit_sdk_subspec, :podspec => 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.10.0/DiditSDK.podspec' ``` ##### Swift Package Manager (optional) diff --git a/SdkReactNative.podspec b/SdkReactNative.podspec index c11160a..1b036e3 100644 --- a/SdkReactNative.podspec +++ b/SdkReactNative.podspec @@ -31,11 +31,21 @@ end didit_sdk_ios_min_version = ["all", "nfc"].include?(didit_sdk_ios_variant) ? max_ios_version.call(min_ios_version_supported, "15.0") : min_ios_version_supported -didit_sdk_subspec = case didit_sdk_ios_variant - when "all" then "DiditSDK/All" - when "core" then "DiditSDK/Core" - when "autodetection" then "DiditSDK/AutoDetection" - when "nfc" then "DiditSDK/NFC" +didit_sdk_ios_location_enabled = ( + defined?($DiditSdkIosLocationEnabled) ? $DiditSdkIosLocationEnabled : + ENV.fetch("DIDIT_SDK_IOS_LOCATION_ENABLED", "false").downcase == "true" +) + +# Location ships as complete variants, never as an add-on: exactly one DiditSDK +# subspec/product may be declared, or two DiditSDK.xcframeworks get vendored. +# `autodetection` is deprecated and identical to `core` since native 4.10.0, so +# it resolves to Core (and to the Core Location variant with Location). +didit_sdk_location_variant = didit_sdk_ios_variant == "autodetection" ? "core" : didit_sdk_ios_variant +didit_sdk_location_suffix = didit_sdk_ios_location_enabled ? "-Location" : "" +didit_sdk_subspec = case didit_sdk_location_variant + when "all" then "DiditSDK/All#{didit_sdk_location_suffix}" + when "core" then "DiditSDK/Core#{didit_sdk_location_suffix}" + when "nfc" then "DiditSDK/NFC#{didit_sdk_location_suffix}" else raise "Invalid DiditSdk iOS variant '#{didit_sdk_ios_variant}'. Set $DiditSdkIosVariant or DIDIT_SDK_IOS_VARIANT to one of: all, core, autodetection, nfc." end @@ -43,11 +53,11 @@ didit_sdk_subspec = case didit_sdk_ios_variant # The sdk-ios SwiftPM products, one per variant. Same binaries as the # CocoaPods subspecs above, resolved from the git tag instead of a raw podspec # URL - see issue #27. -didit_sdk_spm_product = case didit_sdk_ios_variant - when "all" then "DiditSDK" - when "core" then "DiditSDKCore" - when "autodetection" then "DiditSDKAutoDetection" - when "nfc" then "DiditSDKNFC" +didit_sdk_spm_location_suffix = didit_sdk_ios_location_enabled ? "Location" : "" +didit_sdk_spm_product = case didit_sdk_location_variant + when "all" then "DiditSDK#{didit_sdk_spm_location_suffix}" + when "core" then "DiditSDKCore#{didit_sdk_spm_location_suffix}" + when "nfc" then "DiditSDKNFC#{didit_sdk_spm_location_suffix}" end # How the native SDK is resolved: "spm" pulls it straight from the sdk-ios @@ -59,13 +69,6 @@ didit_sdk_ios_linkage = ( ENV.fetch("DIDIT_SDK_IOS_LINKAGE", "cocoapods") ).downcase -didit_sdk_ios_location_enabled = ( - defined?($DiditSdkIosLocationEnabled) ? $DiditSdkIosLocationEnabled : - ENV.fetch("DIDIT_SDK_IOS_LOCATION_ENABLED", "false").downcase == "true" -) -if didit_sdk_ios_location_enabled && didit_sdk_ios_linkage == "spm" - raise "Location requires CocoaPods linkage with the optional DiditSDK/Location subspec." -end if package.dig("diditNativeSdkVersions", "source") Pod::Executable.execute_command('node', [File.join(__dir__, 'native-sdk-source.js'), 'ios']) raise "The pinned native source requires CocoaPods linkage." if didit_sdk_ios_linkage == "spm" @@ -106,8 +109,10 @@ Pod::Spec.new do |s| '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK"', '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/All"', '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/Core"', - '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/AutoDetection"', - '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/NFC"' + '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/NFC"', + '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/Core-Location"', + '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/NFC-Location"', + '"${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/All-Location"' ].join(" ") } @@ -119,7 +124,6 @@ Pod::Spec.new do |s| ) else s.dependency didit_sdk_subspec, didit_sdk_ios_version - s.dependency "DiditSDK/Location", didit_sdk_ios_version if didit_sdk_ios_location_enabled end install_modules_dependencies(s) diff --git a/android/build.gradle b/android/build.gradle index 08e6b20..6ffaa99 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,6 @@ buildscript { ext.SdkReactNative = [ - kotlinVersion: "2.0.21", + kotlinVersion: "2.1.20", minSdkVersion: 24, compileSdkVersion: 36, targetSdkVersion: 36 @@ -65,8 +65,9 @@ switch (diditSdkAndroidVariant) { case "core": diditSdkAndroidArtifact = "didit-sdk-core" break + // Deprecated and identical to core since native 4.10.0 (didit-sdk-autodetection is empty). case "autodetection": - diditSdkAndroidArtifact = "didit-sdk-autodetection" + diditSdkAndroidArtifact = "didit-sdk-core" break case "nfc": diditSdkAndroidArtifact = "didit-sdk-nfc" diff --git a/app.plugin.js b/app.plugin.js index 82aadc2..f39efb8 100644 --- a/app.plugin.js +++ b/app.plugin.js @@ -148,13 +148,8 @@ function diditPodBlock(iosVariant, options, isRubyExpression = false) { ` $DiditSdkIosLocationEnabled = ${options.iosLocationEnabled}`, ]; - if ( - options.iosLinkage === 'spm' && - (options.iosLocationEnabled || pkg.diditNativeSdkVersions.source) - ) { - throw new Error( - 'Location and native source builds require cocoapods iosLinkage.' - ); + if (options.iosLinkage === 'spm' && pkg.diditNativeSdkVersions.source) { + throw new Error('Native source builds require cocoapods iosLinkage.'); } if (options.iosLinkage === 'spm') { @@ -163,12 +158,14 @@ function diditPodBlock(iosVariant, options, isRubyExpression = false) { return lines.join('\n'); } + // Location ships as complete variants: one subspec, never variant + add-on. + // `autodetection` is deprecated and identical to `core` since native 4.10.0. lines.push( + " didit_sdk_location_suffix = $DiditSdkIosLocationEnabled ? '-Location' : ''", ' didit_sdk_subspec = case $DiditSdkIosVariant', - " when 'all' then 'DiditSDK/All'", - " when 'core' then 'DiditSDK/Core'", - " when 'autodetection' then 'DiditSDK/AutoDetection'", - " when 'nfc' then 'DiditSDK/NFC'", + ' when \'all\' then "DiditSDK/All#{didit_sdk_location_suffix}"', + " when 'core', 'autodetection' then \"DiditSDK/Core#{didit_sdk_location_suffix}\"", + ' when \'nfc\' then "DiditSDK/NFC#{didit_sdk_location_suffix}"', ' else', ' raise "Invalid $DiditSdkIosVariant \'#{$DiditSdkIosVariant}\'. Supported values: all, core, autodetection, nfc."', ' end' @@ -178,13 +175,11 @@ function diditPodBlock(iosVariant, options, isRubyExpression = false) { lines.push( ` didit_source_script = Pod::Executable.execute_command('node', ['--print', "require.resolve('@didit-protocol/sdk-react-native/native-sdk-source')"]).strip`, ` didit_source_path = Pod::Executable.execute_command('node', [didit_source_script, 'ios']).strip`, - ' pod didit_sdk_subspec, :path => didit_source_path', - " pod 'DiditSDK/Location', :path => didit_source_path if $DiditSdkIosLocationEnabled" + ' pod didit_sdk_subspec, :path => didit_source_path' ); } else { lines.push( - ` pod didit_sdk_subspec, :podspec => '${options.iosPodspecUrl}'`, - ` pod 'DiditSDK/Location', :podspec => '${options.iosPodspecUrl}' if $DiditSdkIosLocationEnabled` + ` pod didit_sdk_subspec, :podspec => '${options.iosPodspecUrl}'` ); } diff --git a/example-expo/ios/Podfile b/example-expo/ios/Podfile index e5b2cba..340fdeb 100644 --- a/example-expo/ios/Podfile +++ b/example-expo/ios/Podfile @@ -21,22 +21,19 @@ platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' prepare_react_native_project! target 'DiditExpoExample' do -# @generated begin didit-sdk-react-native-pod - expo prebuild (DO NOT MODIFY) sync-d257bf333a2351b6f6bdf0abd31a5390f878696b +# @generated begin didit-sdk-react-native-pod - expo prebuild (DO NOT MODIFY) sync-3b2ae22d68929c05112be279a7f6ad8ec7f7db6d $DiditSdkIosVariant = podfile_properties.fetch('didit.iosVariant', 'all') $DiditSdkIosLinkage = 'cocoapods' $DiditSdkIosLocationEnabled = true + didit_sdk_location_suffix = $DiditSdkIosLocationEnabled ? '-Location' : '' didit_sdk_subspec = case $DiditSdkIosVariant - when 'all' then 'DiditSDK/All' - when 'core' then 'DiditSDK/Core' - when 'autodetection' then 'DiditSDK/AutoDetection' - when 'nfc' then 'DiditSDK/NFC' + when 'all' then "DiditSDK/All#{didit_sdk_location_suffix}" + when 'core', 'autodetection' then "DiditSDK/Core#{didit_sdk_location_suffix}" + when 'nfc' then "DiditSDK/NFC#{didit_sdk_location_suffix}" else raise "Invalid $DiditSdkIosVariant '#{$DiditSdkIosVariant}'. Supported values: all, core, autodetection, nfc." end - didit_source_script = Pod::Executable.execute_command('node', ['--print', "require.resolve('@didit-protocol/sdk-react-native/native-sdk-source')"]).strip - didit_source_path = Pod::Executable.execute_command('node', [didit_source_script, 'ios']).strip - pod didit_sdk_subspec, :path => didit_source_path - pod 'DiditSDK/Location', :path => didit_source_path if $DiditSdkIosLocationEnabled + pod didit_sdk_subspec, :podspec => 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.10.0/DiditSDK.podspec' # @generated end didit-sdk-react-native-pod use_expo_modules! diff --git a/example/ios/Podfile b/example/ios/Podfile index 2f30252..fd9597f 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -34,16 +34,16 @@ target 'SdkReactNativeExample' do else # Developing against a local sdk-ios checkout still goes through CocoaPods. $DiditSdkIosLinkage = 'cocoapods' + # Location ships as complete variants: declare exactly one subspec. + didit_sdk_location_suffix = $DiditSdkIosLocationEnabled ? '-Location' : '' didit_sdk_subspec = case $DiditSdkIosVariant - when 'all' then 'DiditSDK/All' - when 'core' then 'DiditSDK/Core' - when 'autodetection' then 'DiditSDK/AutoDetection' - when 'nfc' then 'DiditSDK/NFC' + when 'all' then "DiditSDK/All#{didit_sdk_location_suffix}" + when 'core', 'autodetection' then "DiditSDK/Core#{didit_sdk_location_suffix}" + when 'nfc' then "DiditSDK/NFC#{didit_sdk_location_suffix}" else raise "Invalid $DiditSdkIosVariant '#{$DiditSdkIosVariant}'. Supported values: all, core, autodetection, nfc." end pod didit_sdk_subspec, :path => didit_sdk_ios_path - pod 'DiditSDK/Location', :path => didit_sdk_ios_path if $DiditSdkIosLocationEnabled end config = use_native_modules! diff --git a/package.json b/package.json index 301927b..edf6c86 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,9 @@ { "name": "@didit-protocol/sdk-react-native", - "version": "4.9.1", + "version": "4.10.0", "diditNativeSdkVersions": { - "ios": "4.9.1", - "android": "4.9.1", - "source": { - "branch": "integration/console-v2", - "revision": "88259c3b2ac57520ead46579ee7c1fb56686cdb1" - } + "ios": "4.10.0", + "android": "4.10.0" }, "description": "React Native wrapper for Didit Identity Verification SDK", "main": "./lib/commonjs/index.js", diff --git a/release-notes/4.10.0.md b/release-notes/4.10.0.md new file mode 100644 index 0000000..2952fcc --- /dev/null +++ b/release-notes/4.10.0.md @@ -0,0 +1,17 @@ +Pins the iOS and Android native SDKs exactly to 4.10.0. + +No JavaScript / TypeScript API changes. The new steps below run inside `startVerification()` / `startVerificationWithWorkflow()` and return the same `VerificationResult`. + +- Both platforms: automatic capture is available in every variant, powered by Didit's own lightweight detectors. TensorFlow Lite on iOS and MediaPipe on Android are removed. The `autodetection` variant is deprecated and identical to `core`, and the Android `didit-sdk-autodetection-play` artifact is no longer published. +- Both platforms: Bank verification step. No setup. The native SDKs open the bank's page (or a page Didit hosts) in the system browser. +- Both platforms: Location verification step. + - iOS: Location is opt-in through complete Location variants (`DiditSDK/Core-Location`, `DiditSDK/NFC-Location`, `DiditSDK/All-Location`, and the SwiftPM products `DiditSDKCoreLocation`, `DiditSDKNFCLocation`, `DiditSDKLocation`). Set `$DiditSdkIosLocationEnabled = true` (or `iosLocationEnabled: true` in the Expo plugin) and this package selects the matching variant. CocoaPods and SwiftPM are both supported. The separate `DiditSDK/Location` add-on is no longer used. + - Android: `didit-sdk-core` declares `ACCESS_FINE_LOCATION` and `ACCESS_COARSE_LOCATION`. Remove them with `tools:node="remove"` if your app never runs the step. +- Both platforms: electronic signature step (Sign documents). +- Both platforms: biometric consent screen when Didit's backend requires it for the workflow. +- Both platforms: a "No camera found" screen with a Copy link action to continue on another device. +- Both platforms: verification steps the installed SDK does not support show update-app guidance and return a failed result instead of stalling. +- Android: active liveness recovers after an interrupted or failed attempt. +- iOS: smaller framework, with translations moved out of the binary. +- Android toolchain: native 4.10.0 requires Kotlin 2.1.10+, Android Gradle Plugin 8.6.0+, Gradle 8.7+ and JDK 17. The wrapper's default Kotlin version is now 2.1.20. +- Development builds: the temporary native source pin used during development is removed. The package resolves released native 4.10.0 binaries only. diff --git a/src/__tests__/location-plugin.test.ts b/src/__tests__/location-plugin.test.ts index 5d99c8c..f0e597e 100644 --- a/src/__tests__/location-plugin.test.ts +++ b/src/__tests__/location-plugin.test.ts @@ -20,18 +20,55 @@ it('keeps Location opt-in even with the all variant', async () => { expect(await podfile()).toContain('$DiditSdkIosLocationEnabled = false'); }); -it('uses the verified source for both the selected variant and Location', async () => { +const SOURCE_PIN = { branch: 'dev', revision: 'a'.repeat(40) }; + +async function podfileWithSource(props: object) { + jest.resetModules(); + jest.doMock('../../package.json', () => ({ + ...jest.requireActual('../../package.json'), + diditNativeSdkVersions: { + ...jest.requireActual('../../package.json').diditNativeSdkVersions, + source: SOURCE_PIN, + }, + })); + const plugin = require('../../app.plugin.js'); + const config = plugin({ name: 'test', slug: 'test' }, props); + const result = await config.mods.ios.podfile({ + ...config, + modResults: { contents: "target 'App' do\n use_expo_modules!\nend" }, + modRequest: { introspect: true }, + }); + jest.dontMock('../../package.json'); + + return result.modResults.contents as string; +} + +it('declares one released pod line with the mapped Location subspec', async () => { const contents = await podfile({ iosLocationEnabled: true }); expect(contents).toContain('$DiditSdkIosLocationEnabled = true'); - expect(contents).toContain('native-sdk-source'); + expect(contents).toContain('"DiditSDK/All#{didit_sdk_location_suffix}"'); expect(contents).toContain( - 'pod didit_sdk_subspec, :path => didit_source_path' + `when 'core', 'autodetection' then "DiditSDK/Core#{didit_sdk_location_suffix}"` ); + expect(contents).toContain('"DiditSDK/NFC#{didit_sdk_location_suffix}"'); + expect(contents).not.toContain('DiditSDK/AutoDetection'); + expect(contents.match(/^\s*pod /gm)).toHaveLength(1); + expect(contents).toContain(`pod didit_sdk_subspec, :podspec => '`); + expect(contents).not.toContain("pod 'DiditSDK/Location'"); + expect(contents).not.toContain('native-sdk-source'); +}); + +it('declares one source pod line with the mapped Location subspec', async () => { + const contents = await podfileWithSource({ iosLocationEnabled: true }); + + expect(contents).toContain('native-sdk-source'); + expect(contents.match(/^\s*pod /gm)).toHaveLength(1); expect(contents).toContain( - "pod 'DiditSDK/Location', :path => didit_source_path" + 'pod didit_sdk_subspec, :path => didit_source_path' ); expect(contents).not.toContain(':podspec'); + expect(contents).not.toContain('DiditSDK/Location'); }); it('updates the Location opt-in without duplicating the generated block', async () => { @@ -40,13 +77,23 @@ it('updates the Location opt-in without duplicating the generated block', async expect(disabled).toBe(await podfile({ iosLocationEnabled: false }, disabled)); expect(disabled).not.toContain('$DiditSdkIosLocationEnabled = true'); - expect(disabled.match(/pod 'DiditSDK\/Location'/g)).toHaveLength(1); + expect(disabled.match(/pod didit_sdk_subspec/g)).toHaveLength(1); +}); + +it('allows SPM linkage with Location and declares no pod line', async () => { + const contents = await podfile({ + iosLocationEnabled: true, + iosLinkage: 'spm', + }); + + expect(contents).toContain('$DiditSdkIosLocationEnabled = true'); + expect(contents).not.toContain('pod didit_sdk_subspec'); }); -it('rejects SPM instead of omitting the native Location module', async () => { - await expect( - podfile({ iosLocationEnabled: true, iosLinkage: 'spm' }) - ).rejects.toThrow('cocoapods'); +it('rejects SPM with a native source pin', async () => { + await expect(podfileWithSource({ iosLinkage: 'spm' })).rejects.toThrow( + 'cocoapods' + ); }); it('adds the Android source settings once across prebuilds', async () => { diff --git a/src/__tests__/native-sdk-pins.test.ts b/src/__tests__/native-sdk-pins.test.ts index 36d0f43..db7215e 100644 --- a/src/__tests__/native-sdk-pins.test.ts +++ b/src/__tests__/native-sdk-pins.test.ts @@ -167,6 +167,27 @@ describe('swift package manager linkage', () => { }); }); +describe('Location variants', () => { + const podspec = read('SdkReactNative.podspec'); + + it('declares no separate DiditSDK/Location dependency', () => { + expect(podspec).not.toContain('"DiditSDK/Location"'); + }); + + it.each(['Core-Location', 'NFC-Location', 'All-Location'])( + 'searches the %s framework folder', + (variant) => { + expect(podspec).toContain( + `\${PODS_XCFRAMEWORKS_BUILD_DIR}/DiditSDK/${variant}` + ); + } + ); + + it('pins no leftover development source', () => { + expect(pkg.diditNativeSdkVersions.source).toBeUndefined(); + }); +}); + describe('hand-maintained pins stay in lockstep', () => { it.each(['README.md'])( '%s references only the declared iOS version', diff --git a/src/__tests__/native-source.test.ts b/src/__tests__/native-source.test.ts index dbfb782..26ba29c 100644 --- a/src/__tests__/native-source.test.ts +++ b/src/__tests__/native-source.test.ts @@ -7,7 +7,16 @@ jest.mock('fs', () => ({ readFileSync: jest.fn(), })); -const { diditNativeSdkVersions } = require('../../package.json'); +// The released package has no source pin; the mechanism is exercised with a fixture. +const mockPin = { branch: 'dev', revision: 'b'.repeat(40) }; +jest.mock('../../package.json', () => ({ + diditNativeSdkVersions: { + ios: '0.0.0', + android: '0.0.0', + source: { branch: 'dev', revision: 'b'.repeat(40) }, + }, +})); +const diditNativeSdkVersions = { source: mockPin }; const { resolveNativeSdkSource, resolveAndroidRepository, @@ -21,8 +30,19 @@ afterEach(() => { jest.clearAllMocks(); }); -it('requires an immutable revision for the integration branch', () => { - expect(diditNativeSdkVersions.source.branch).toBe('integration/console-v2'); +it('is a no-op without a source pin', () => { + jest.resetModules(); + jest.doMock('../../package.json', () => ({ + diditNativeSdkVersions: { ios: '0.0.0', android: '0.0.0' }, + })); + const unpinned = require('../../native-sdk-source'); + + expect(unpinned.resolveNativeSdkSource('ios')).toBe(''); + expect(unpinned.resolveAndroidRepository()).toBe(''); + expect(git).not.toHaveBeenCalled(); +}); + +it('requires an immutable revision for the pin', () => { expect(diditNativeSdkVersions.source.revision).toMatch(/^[a-f0-9]{40}$/); });