Skip to content

add rn-sdk example app and fix capture package consumption - #2245

Open
seshanthS wants to merge 2 commits into
devfrom
feat/wia-app-native-modules
Open

add rn-sdk example app and fix capture package consumption#2245
seshanthS wants to merge 2 commits into
devfrom
feat/wia-app-native-modules

Conversation

@seshanthS

@seshanthS seshanthS commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds packages/rn-sdk-example-app, a consumer example app exercising the full @selfxyz/rn-sdk flow — WebView-driven MRZ + NFC onboarding (self-app mode), direct-to-disclose mode, and standalone usage of @selfxyz/rn-mrz-scanner / @selfxyz/rn-nfc-passport — validated on-device (Android MRZ scan + NFC chip read completed; iOS Debug device build green with real capture binaries).
  • Fixes three packaging bugs that made the capture packages unusable by any consumer, all found by this integration: Swift could not see the vendored xcframework modules (both scanners silently compiled as unavailable stubs; NFC failed to link), the dist Mixpanel swiftinterface fails to compile, and Metro resolved the ESM build where lazy require('react-native') silently throws.
  • The example app deliberately does not link the self-sdk-swift SPM package (KMP bridge out of scope) — its transitive SPM OpenSSL/NFCPassportReader/Mixpanel collide with the CocoaPods-delivered copies.

Changes

RN SDK packaging (packages/rn-mrz-scanner, packages/rn-nfc-passport, packages/rn-sdk)

  • Podspecs: add per-SDK SWIFT_INCLUDE_PATHS pointing at the vendored xcframework slices' Headers/ so canImport(SelfSdkNfc/SelfSdkOcr) is true when the binaries are present. CocoaPods' XCFrameworkIntermediates copy cannot serve this — its per-framework rsync --delete clobbers the merged Headers dir.
  • rn-nfc-passport postinstall: patch the downloaded Mixpanel/SelfNFCPassportReader swiftinterfaces, stripping the Mixpanel. self-qualification that fails to compile (interfaces were emitted without -module-interface-preserve-types-as-written; the class shadows the module). Becomes a no-op once fixed artifacts ship from self-sdk-dist.
  • All three package.jsons: add a "react-native" exports condition pointing at the CJS build. Metro previously resolved the import condition, where tsup turns lazy require('react-native') into a __require shim Metro never registers — so NativeModules resolution silently returned undefined (rn-nfc-passport entirely; rn-sdk's optional peers nfc-manager/biometrics/keychain/react-native-fs).

New example app (packages/rn-sdk-example-app)

  • Cloned from rn-sdk-test-app's known-good RN 0.83.9 + New Arch config (Podfile RCT_NEW_ARCH_ENABLED xcconfig fix, self-wallet asset copy build phase, token-gated self-sdk-dist maven, NFC entitlement + TECH_DISCOVERED filter, monorepo metro blockList).
  • App.tsx: capability panel, self-app onboarding launch, and a disclose-mode launch (request with disclosures routes the WebView straight to /disclose/request).
  • DirectCaptureScreen.tsx: drives the capture packages directly — startScanning() for MRZ, then platform-split scanPassport/scan chip read with the MRZ-derived BAC keys.
  • Explicit iOS bundle id / Android appId com.selfxyz.rnexample (NFC entitlement needs an explicit App ID); no SelfSdkSwift SPM reference.

Config/infra

  • turbo.json: @selfxyz/rn-sdk-example-app#build entry.
  • packages/rn-sdk-test-app/metro.config.cjs: blockList the new app's react copies (and vice versa) to avoid duplicate-React failures.
  • pnpm-lock.yaml for the new workspace.

Test Plan

  • pnpm lint && pnpm types passes
  • pnpm --filter @selfxyz/rn-sdk-example-app types passes
  • iOS: pnpm --filter @selfxyz/rn-sdk build && pnpm --filter @selfxyz/rn-mrz-scanner build && pnpm --filter @selfxyz/rn-nfc-passport build, pod install, then Debug device build succeeds with SELF_SDK_GITHUB_TOKEN set (verified locally: BUILD SUCCEEDED; bundle contains self-wallet/index.html, one OpenSSL on the link line)
  • Android: ./gradlew :app:assembleDebug with token set; on-device capability panel shows mrzCamera: available / nfc: available (verified locally: MRZ scan + NFC chip read completed on a physical device)
  • Fresh-install path: delete packages/rn-nfc-passport/ios/Frameworks, re-run postinstall, interfaces come back patched (verified locally)

Native Consolidation Checklist

  • CONTRACTS.md reviewed - no unintended contract changes
  • Layer 1 bridge contract tests pass (cd app && yarn jest:run / yarn workspace @selfxyz/rn-sdk-test-app test)
  • Layer 3 builds pass (app iOS, RN test app iOS, RN test app Android)
  • Layer 4 manual smoke test signed off (if consolidation PR)
  • No new native business logic added (logic belongs in TypeScript)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a React Native example app demonstrating onboarding, disclosure requests, direct MRZ scanning, NFC passport capture, and verification results on iOS and Android.
    • Added support for React Native package resolution across the SDK, MRZ scanner, and NFC passport modules.
  • Bug Fixes
    • Improved Swift module discovery for vendored iOS frameworks.
    • Improved framework compatibility by cleaning unsupported Swift interface qualifiers.
  • Documentation
    • Added setup, prerequisites, and build instructions for the example app.

seshanthS and others added 2 commits July 31, 2026 17:39
…mers

Three packaging bugs found by integrating the packages into a fresh app:

- Podspecs: vendored static xcframeworks' Swift modules (Headers/*.swiftmodule)
  were never on the Swift frontend's search path, so canImport(SelfSdkNfc/
  SelfSdkOcr) was silently false and both modules compiled as unavailable
  stubs even with the binaries present; the NFC pod additionally failed to
  link (SELF_NFC_AVAILABLE-guarded ObjC extern module references the
  compiled-out Swift class). CocoaPods' XCFrameworkIntermediates copy can't
  serve as the search path — its per-framework rsync --delete clobbers the
  merged Headers dir — so SWIFT_INCLUDE_PATHS points at the vendored slices
  directly, per SDK.

- postinstall (rn-nfc-passport): the dist Mixpanel/SelfNFCPassportReader
  swiftinterfaces were emitted without
  -module-interface-preserve-types-as-written, so Mixpanel-module types are
  self-qualified (Mixpanel.Properties) and shadowed by the Mixpanel class,
  failing interface compilation. Strip the qualifier after extraction;
  becomes a no-op once fixed artifacts ship from self-sdk-dist.

- package.json exports: Metro resolves the "import" condition to the tsup
  ESM build, where lazy require('react-native') becomes a __require shim
  Metro never registers as a dependency — it throws at runtime and the
  try/catch returns undefined, so NativeModules resolution silently fails
  (rn-nfc-passport entirely; rn-sdk's optional peers: nfc-manager,
  biometrics, keychain, react-native-fs). Add a "react-native" exports
  condition pointing at the CJS build; Node/Vite are unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…NFC flow

New workspace cloned from rn-sdk-test-app's known-good RN 0.83.9 + New Arch
config, demonstrating third-party consumption of @selfxyz/rn-sdk with the
optional capture packages:

- SelfVerification in self-app mode (WebView-driven MRZ camera scan + NFC
  chip read onboarding) and disclose mode (request with disclosures routes
  straight to /disclose/request)
- DirectCaptureScreen: standalone package usage — startScanning() for MRZ,
  then scan/scanPassport with the MRZ-derived BAC keys
- capability panel via isMrzScannerAvailable/isSelfPassportReaderAvailable

Unlike the test app, the Xcode project does NOT link the self-sdk-swift SPM
package — its transitive SPM OpenSSL/NFCPassportReader/Mixpanel collide with
the CocoaPods-delivered copies ("Multiple commands produce OpenSSL.framework").
The KMP bridge path is intentionally out of scope here.

Validated: iOS Debug device build succeeds with real capture binaries;
Android debug build installs and completes MRZ scan + NFC chip read
on-device. Both apps' metro blockLists updated to exclude each other's
react copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
self-webview-app Ignored Ignored Jul 31, 2026 12:16pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds React Native export conditions and native framework integration fixes. It also adds a complete React Native example app with verification, MRZ capture, NFC capture, Android configuration, iOS configuration, and workspace build settings.

Changes

SDK integration fixes

Layer / File(s) Summary
React Native package exports
packages/rn-mrz-scanner/package.json, packages/rn-nfc-passport/package.json, packages/rn-sdk/package.json
The package export maps now provide React Native entry points at ./dist/index.js.
Vendored framework Swift integration
packages/rn-nfc-passport/scripts/postinstall.js, packages/rn-mrz-scanner/selfxyz-rn-mrz-scanner.podspec, packages/rn-nfc-passport/selfxyz-rn-nfc-passport.podspec
The NFC postinstall script patches Mixpanel. qualifiers in Swift interfaces. Both podspecs configure device and simulator Swift header paths.

React Native example app

Layer / File(s) Summary
Verification and direct capture flows
packages/rn-sdk-example-app/App.tsx, packages/rn-sdk-example-app/DirectCaptureScreen.tsx
The app supports Self App verification, disclosure requests, MRZ scanning, NFC passport reading, cancellation, capability checks, and result states.
Workspace configuration
packages/rn-sdk-example-app/package.json, packages/rn-sdk-example-app/metro.config.cjs, packages/rn-sdk-example-app/tsconfig.json, packages/rn-sdk-example-app/README.md, turbo.json, packages/rn-sdk-test-app/metro.config.cjs, packages/rn-sdk-example-app/*
The example package, Metro, TypeScript, Ruby, documentation, ignore rules, entry point, and Turbo task configuration define development and build behavior.
Android application setup
packages/rn-sdk-example-app/android/*
The Android project configures React Native, NFC handling, permissions, dependencies, resources, Gradle tooling, and SDK repository access.
iOS application setup
packages/rn-sdk-example-app/ios/*
The iOS project configures CocoaPods, Xcode targets, React Native startup, app resources, privacy declarations, NFC entitlements, and installation recovery.

Estimated code review effort: 5 (Critical) | ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: adding the RN SDK example app and fixing capture package consumption.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wia-app-native-modules

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a571820e-77d7-4315-b123-8f53a5c724f5

📥 Commits

Reviewing files that changed from the base of the PR and between 6ef1d6f and d053669.

⛔ Files ignored due to path filters (15)
  • packages/rn-sdk-example-app/Gemfile.lock is excluded by !**/*.lock
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png is excluded by !**/*.png
  • packages/rn-sdk-example-app/android/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
  • packages/rn-sdk-example-app/ios/Podfile.lock is excluded by !**/*.lock
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp.xcworkspace/contents.xcworkspacedata is excluded by !**/*.xcworkspace/contents.xcworkspacedata
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • packages/rn-mrz-scanner/package.json
  • packages/rn-mrz-scanner/selfxyz-rn-mrz-scanner.podspec
  • packages/rn-nfc-passport/package.json
  • packages/rn-nfc-passport/scripts/postinstall.js
  • packages/rn-nfc-passport/selfxyz-rn-nfc-passport.podspec
  • packages/rn-sdk-example-app/.gitignore
  • packages/rn-sdk-example-app/App.tsx
  • packages/rn-sdk-example-app/DirectCaptureScreen.tsx
  • packages/rn-sdk-example-app/Gemfile
  • packages/rn-sdk-example-app/README.md
  • packages/rn-sdk-example-app/android/app/build.gradle
  • packages/rn-sdk-example-app/android/app/debug.keystore
  • packages/rn-sdk-example-app/android/app/proguard-rules.pro
  • packages/rn-sdk-example-app/android/app/src/debug/AndroidManifest.xml
  • packages/rn-sdk-example-app/android/app/src/main/AndroidManifest.xml
  • packages/rn-sdk-example-app/android/app/src/main/java/com/selfxyz/rnexample/MainActivity.kt
  • packages/rn-sdk-example-app/android/app/src/main/java/com/selfxyz/rnexample/MainApplication.kt
  • packages/rn-sdk-example-app/android/app/src/main/res/drawable/rn_edit_text_material.xml
  • packages/rn-sdk-example-app/android/app/src/main/res/values/strings.xml
  • packages/rn-sdk-example-app/android/app/src/main/res/values/styles.xml
  • packages/rn-sdk-example-app/android/app/src/main/res/xml/nfc_tech_filter.xml
  • packages/rn-sdk-example-app/android/build.gradle
  • packages/rn-sdk-example-app/android/gradle.properties
  • packages/rn-sdk-example-app/android/gradle/libs.versions.toml
  • packages/rn-sdk-example-app/android/gradle/wrapper/gradle-wrapper.properties
  • packages/rn-sdk-example-app/android/gradlew
  • packages/rn-sdk-example-app/android/gradlew.bat
  • packages/rn-sdk-example-app/android/settings.gradle
  • packages/rn-sdk-example-app/app.json
  • packages/rn-sdk-example-app/babel.config.cjs
  • packages/rn-sdk-example-app/index.js
  • packages/rn-sdk-example-app/ios/.xcode.env
  • packages/rn-sdk-example-app/ios/Podfile
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp.xcodeproj/project.pbxproj
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp.xcodeproj/xcshareddata/xcschemes/SelfRNExampleApp.xcscheme
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/AppDelegate.swift
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/Images.xcassets/Contents.json
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/Info-Debug.plist
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/Info.plist
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/LaunchScreen.storyboard
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/PrivacyInfo.xcprivacy
  • packages/rn-sdk-example-app/ios/SelfRNExampleApp/SelfRNExampleApp.entitlements
  • packages/rn-sdk-example-app/ios/scripts/pod-install-with-cache-fix.sh
  • packages/rn-sdk-example-app/metro.config.cjs
  • packages/rn-sdk-example-app/package.json
  • packages/rn-sdk-example-app/react-native.config.cjs
  • packages/rn-sdk-example-app/tsconfig.json
  • packages/rn-sdk-test-app/metro.config.cjs
  • packages/rn-sdk/package.json
  • turbo.json

Comment on lines +159 to 160
patchSwiftInterfaces(name);
log(`installed ios/Frameworks/${name}.xcframework`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Patch existing xcframework installations.

patchSwiftInterfaces(name) runs only after a new extraction. main() skips existing .xcframework directories before this call. An existing workspace installation can therefore retain the invalid interfaces and disable NFC capture.

Call patchSwiftInterfaces(name) before continuing for an existing framework.

Comment on lines +16 to +17
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Add the referenced launcher icon resources.

Line 16 and Line 17 reference @mipmap/ic_launcher and @mipmap/ic_launcher_round. The supplied resource set for this new app contains no matching mipmap resources. Android resource linking will fail before the app can build.

Add both launcher icon resources or reference existing resources.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d05366911f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`${name}.xcframework missing after extracting ${name}-${version}.xcframework.zip`
);
}
patchSwiftInterfaces(name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Patch cached frameworks before returning

When ios/Frameworks already contains all three artifacts—such as after pulling this change into an existing SDK checkout—shouldSkip() returns frameworks already present and main() exits before this new patch runs. Those cached swiftinterfaces therefore retain the broken Mixpanel. qualification, so rerunning pnpm install leaves NFC unavailable; apply the patch to existing artifacts before the early return, not only immediately after extraction.

Useful? React with 👍 / 👎.

Comment on lines +20 to +21
the same token-gated Maven repo. **Without the token the app still builds, but both capture
modules compile as stubs and the capability panel shows `unavailable`.**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not promise tokenless Android builds

On a clean machine without locally published SDK artifacts, omitting SELF_SDK_GITHUB_TOKEN removes the only repository that can provide the private xyz.self.sdk:* dependencies, while the app and capture modules declare those dependencies unconditionally (for example xyz.self.sdk:shared:0.1.3). Gradle consequently fails dependency resolution rather than compiling capture stubs, so the documented tokenless behavior is only true for iOS or for developers who already populated mavenLocal().

Useful? React with 👍 / 👎.

Comment on lines +50 to +51
<key>NFCReaderUsageDescription</key>
<string>Needed to read passport NFC chip for identity verification.</string>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the passport AID to both iOS plists

When the example runs its primary NFC flow on an iPhone, both Info-Debug.plist and Info.plist provide only the usage description and omit com.apple.developer.nfc.readersession.iso7816.select-identifiers. The reader therefore cannot select the eMRTD ISO-7816 applet; add A0000002471001 as done by app/ios/Self/Info.plist, including in the Debug plist used by the documented device workflow.

Useful? React with 👍 / 👎.

CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = SelfRNExampleApp/SelfRNExampleApp.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5B29R5LYHQ;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the committed Apple development team

A third-party developer opening this example inherits team 5B29R5LYHQ, which they do not control, so physical-device signing initially fails until the project file is edited. This also contradicts the README instruction to set the team locally and not commit DEVELOPMENT_TEAM; remove the setting from both Debug and Release so each consumer can select their own team without carrying a repository change.

Useful? React with 👍 / 👎.

Comment on lines +17 to +18
- `SELF_SDK_GITHUB_TOKEN` (a GitHub token with `read:packages` on `selfxyz/self-sdk-dist`)
exported **before `pnpm install`**. The capture packages' postinstall downloads the iOS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require repository access for iOS framework downloads

A token granted only read:packages can authenticate to the Maven package registry but cannot read releases from the private selfxyz/self-sdk-dist repository. Both iOS postinstall scripts call the private repository Releases API, so a developer following this prerequisite receives a 404 and silently gets unavailable capture stubs; document private-repository/Contents read access in addition to read:packages.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant