Skip to content

Prepare the wrapper for the Bank and Location steps and report retryBlocked on iOS - #26

Merged
arosasg merged 5 commits into
mainfrom
botinc/run-9f28d4de
Oct 10, 2026
Merged

arosasg merged 5 commits into
mainfrom
botinc/run-9f28d4de

Conversation

@botinc-agent

@botinc-agent botinc-agent Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

On hold for the native Bank and Location release: do not merge. Review is paused until that release exists. The two open review findings below are the merge gate. The native SDK pins stay at the published 4.9.1 until then. When the release exists, the pin bump lands on this pull request, both findings are checked again, and the pull request is squash-merged with its title as the commit title and the pre-filled commit description replaced.

Open review findings (the merge gate)

  1. No released native SDK has Bank and Location yet. Both native pins are 4.9.1, and the README notes that 4.9.1 lacks these steps. Location is only reachable through the example app's opt-in native source override. Checked on 2026-10-08 at 07:07 UTC: 4.9.1 is still the newest sdk-ios and sdk-android release, and the native pull requests that add the steps are still open. Clears when the pins move to that release and the consumer setup, the Bank and Location flows, and the native retryBlocked callback are tested together against it.
  2. The branch commit messages carry an internal tracker reference. Automation writes these messages. The published branch is not rewritten (no force push). Clears at merge: squash only, with this pull request's title as the commit title and the pre-filled description replaced by the summary under "Merge gate", so none of the messages reaches main.

Public metadata: the title and the visible description of this pull request and of #24 are clean. What is left is handled at merge: the commit messages, by the squash above, and the two hidden HTML comment lines around "Visual proof", which automation rewrites at each checkpoint. Delete those two lines just before merging.

Summary

Workflows can include a Bank verification step and a Location verification step. The native iOS and Android SDKs draw both screens and report the same VerificationResult as every other step, so the Dart API does not change. This pull request prepares the wrapper for them, and makes the iOS bridge report the native retryBlocked error the way Android already does.

  • README: iOS location keys (NSLocationWhenInUseUsageDescription, plus the optional precise-location purpose key DiditLocationVerification) and their translations. Android location permissions (merged from the native SDK) and how to remove them. App Store and Google Play privacy declarations. A "Bank and Location Steps" section with an availability note. retryBlocked in the error types table.
  • iOS retryBlocked: the iOS plugin used to report the native VerificationError.retryBlocked as unknown. It now reports retryBlocked, as the Android plugin does, so Dart receives VerificationErrorType.retryBlocked with the declined session on both platforms. The Dart types, the result map and Android are unchanged. The native iOS 4.9.1 binaries the plugin pins (all four variants) declare this case.
  • iOS bridge test, Dart to native and back (example/integration_test/retry_blocked_test.dart, run by tool/ios_bridge_test.sh in a new "Bridge test (iOS)" CI step):
    • Dart calls DiditSdk.startVerificationWithWorkflow. The plugin presents the native SDK the way it does in an app, and the released native 4.9.1 SDK sends its real session request.
    • The request goes to tool/stand_in_api.py, a local stand-in for the verification API. The script maps the API host to it in /etc/hosts and has the simulator trust a throwaway certificate authority. The stand-in gives the answer the API gives a person whose earlier session was declined and who has no retries left (status: Declined, can_retry: false).
    • The native SDK ends the flow through its own .diditVerification callback with .failed(.retryBlocked, declined session). The test asserts in Dart that the result is VerificationFailed with VerificationErrorType.retryBlocked, the stand-in's session id and VerificationStatus.declined.
    • Only the server is replaced: the Dart API, the method channel, the plugin, the native SDK binary and its callback are the real ones. Without the stand-in the test is skipped, so it never reaches the real API.
    • It runs on every pull request, so the pin bump to the native release with Bank and Location repeats it against the new binaries.
  • Example app:
    • Location usage strings in English (Info.plist) and Spanish (es.lproj/InfoPlist.strings, bundled in the Xcode project).
    • An opt-in DIDIT_SDK_IOS_SOURCE Podfile hook that builds the native SDK from a source tree with its DiditSDK/Location module. An empty value counts as unset.
    • --dart-define=DIDIT_LANGUAGE=<code> to pick the verification language.
  • Android plugin tests (JVM, MockK against a stubbed native DiditSdk), run by the Android CI job:
    • The call reaches the native SDK with the token, or the workflow id and vendor data, and the Dart configuration (Spanish included).
    • The native UI opens once the session is ready. It does not open after a native start error, or when there is no activity.
    • Every result shape reaches Dart as the map VerificationResult.fromMap reads, retryBlocked included.
  • iOS plugin tests (XCTest in example/ios/RunnerTests), run by the iOS CI job on a simulator against the released native binaries: argument validation, configuration parsing (Spanish) and result mapping, retryBlocked included. The test target builds for iOS 15, the minimum the default native variant needs, so it can import the plugin module.
  • Dart tests: a retryBlocked failure answered over the method channel resolves startVerificationWithWorkflow to VerificationFailed with VerificationErrorType.retryBlocked and the declined session. Also a Spanish device locale on both entry points, a declined session, an unsupported-step failure, and the example app's location setup.
  • Example app Podfile.lock: its PODFILE CHECKSUM now matches the edited Podfile (CocoaPods records the SHA-1 of the Podfile). The resolved pods are unchanged.
  • Variant packages regenerated (their Swift copies match the root source). CONTRIBUTING.md lists how to run each test suite, the iOS bridge test included.

Merge gate: waits for the native release

Do not merge this pull request until a native SDK release that includes both steps exists, and this pull request's pins and its iOS Location consumer setup have been moved to that release and tested together.

  • The native SDK pins stay at the published 4.9.1 until then. Checked again on 2026-10-08 at 07:07 UTC: 4.9.1 (released 2026-10-06) is still the latest native release on both platforms (the newest sdk-ios tag and GitHub release, the newest sdk-android GitHub release, and the newest me.didit:didit-sdk version in the sdk-android Maven repository), and it includes neither step. The native pull requests that add the steps are still open. Pinning an unpublished version would break every consumer build and both example builds in CI.

  • The pin bump also drops the README availability note, adds the changelog entry, and adds the consumer switch for the iOS DiditSDK/Location module (CocoaPods and Swift Package Manager), which the released binaries do not include yet. CI then reruns the iOS bridge test, the XCTests and both example builds against the new binaries.

  • Squash-merge only, and replace the pre-filled commit description. Do not use a merge commit or a rebase merge. The branch's commit messages come from automation and carry an internal tracker reference that this repository's privacy rules keep off main. A squash keeps them out of the default branch's history only if the description is replaced too: this repository's squash dialog pre-fills the description with every branch commit message. Use the pull request title as the commit title and the summary below as the description (or leave it empty). The published branch is not rewritten (no force push).

    Prepare the wrapper for the native Bank and Location steps: README permission, privacy and availability guidance, example app location strings in English and Spanish, and an opt-in Podfile hook for a native source tree. Report the native retryBlocked error on iOS as Android does, with an iOS bridge test and plugin tests on both platforms.

    The same merge from the command line (it sets both fields, so nothing is pre-filled); the pin-bump revision updates the summary:

    gh api -X PUT repos/didit-protocol/sdk-flutter/pulls/26/merge -f merge_method=squash \
      -f commit_title="$(gh pr view 26 --json title --jq .title) (#26)" \
      -f commit_message="<the summary above>"
    

Screens

The native SDKs draw every Bank and Location screen, including the location permission request and its denied states, and the retry-limit screen. Their JVM screenshot tests (light, dark, Spanish) live with the native SDK. The wrapper draws nothing of its own, so there is no wrapper screen to capture. The visual proof below is the design's iOS permission frame, whose prompt body is the string this example app declares.

Validation

$ flutter analyze
No issues found!
$ flutter test -r expanded
00:01 +26: All tests passed!   # rerun on this revision, 2026-10-08 07:08 UTC
$ flutter analyze   # in packages/didit_sdk_core, packages/didit_sdk_autodetection, packages/didit_sdk_nfc, example
No issues found!    # each
$ dart format --output=none --set-exit-if-changed example/integration_test/retry_blocked_test.dart
Formatted 1 file (0 changed)
$ shellcheck tool/ios_bridge_test.sh && bash -n tool/ios_bridge_test.sh   # clean
  • Pinned native contract (checked again 2026-10-08): the plugin's mapErrorType was read against the sdk-ios 4.9.1 binaries it resolves. Those are the four Swift Package Manager zips, whose SHA-256 matches the checksums in sdk-ios Package.swift, and DiditSDK-CocoaPods.zip, which the podspec downloads. In every variant (All, Core, AutoDetection, NFC), VerificationError declares exactly sessionExpired, retryBlocked, networkError, cameraAccessDenied and unknown(String). The plugin maps each case by name; @unknown default only guards cases added by later native releases.
  • Stand-in API (checked on Linux): the certificates the script generates pass openssl verify -purpose sslserver and meet iOS's rules for TLS server certificates (RSA 2048, SHA-256, DNS subject alternative name, server-auth extended key usage, 2-day validity). Over TLS with only that authority trusted, the session request answers 200 with the retry-blocked session on IPv4 and IPv6, any other path answers 404, a client that does not trust the authority is refused, and the refusal is logged without stopping the server. The response has every field the native SDK's session model requires, and the native SDK's retry-blocked check (can_retry == false and status Declined) matches it.
  • iOS bridge test, on a CI simulator (CI run 37717798127 on this revision, job "Example app builds (iOS)", step "Bridge test (iOS)"; this Linux environment has no Xcode). The test ran, it was not skipped. The stand-in logged both the host's own reachability check and the session request the native SDK sent from the simulator:
    $ bash tool/ios_bridge_test.sh
    Xcode build done.                                           102.3s
    ✅ the native retryBlocked callback reaches Dart as retryBlocked
    🎉 1 test passed.
    Requests the stand-in API received:
    POST /v1/session/unilink/bridge-check/ 200
    POST /v1/session/unilink/retry-blocked-workflow/ 200
    
    Without the plugin's retryBlocked case, this test receives VerificationErrorType.unknown and fails.
  • iOS plugin tests: the XCTests in the same job end with ** TEST SUCCEEDED **.
  • Android plugin tests: 9 of 9 pass in the Android CI job (./gradlew :didit_sdk:testDebugUnitTest, BUILD SUCCESSFUL in run 37717798127).
  • This revision only changes example/ios/Podfile.lock's checksum line, so no Dart, Kotlin or Swift source changes. CI reran the iOS bridge test, the XCTests and the Android tests above on it (run 37717798127), and all four jobs passed.

Visual proof

Bank and Location steps in the Flutter wrapper

The wrapper draws no Bank or Location screen: the native SDKs draw both. This change adds no UI code. Its Kotlin and Swift changes are plugin unit tests, the helpers they reach, and the iOS mapping of the native retryBlocked error. The only text it controls that a person sees is the body of the iOS location permission prompt. The example app declares it in English (Info.plist) and Spanish (es.lproj/InfoPlist.strings).

These are the design's iOS permission frame for the Location step, the spec those strings follow. In each frame the prompt body is byte-identical to the string committed in the example app. iOS writes the title line itself from the app's name; only the body comes from the app.

Light, English Dark, Spanish
Design spec, light, English Design spec, dark, Spanish
Language Prompt body committed in the example app
English Your location is used to confirm where you are for this verification.
Spanish Tu ubicación se usa para confirmar dónde estás en esta verificación.

Nothing visible changed in this revision. It changes no file in the repository; it only records a new check of the native releases in the pull request description. The prompt strings above are unchanged and still match the example app byte for byte.

Not captured here. This Linux sandbox has no Xcode and no Android emulator, so there is no device capture through the wrapper yet. A device capture needs a Mac or an Android device, plus a native SDK release that includes the steps. The native retryBlocked callback reaching Dart is proven by the iOS bridge test on a CI simulator (see Validation in the pull request).

🤖 Generated with Claude Code

…ntinuation of DID-5660)

Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
@botinc-agent botinc-agent Bot changed the title DID-5768: [H11] Flutter SDK: Bank and Location steps pass-through (continuation of DID-5660) Prepare the wrapper for the Bank and Location steps: docs, example app setup and plugin tests Oct 7, 2026
…ntinuation of DID-5660)

Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
@botinc-agent botinc-agent Bot changed the title Prepare the wrapper for the Bank and Location steps: docs, example app setup and plugin tests Prepare the wrapper for the Bank and Location steps and report retryBlocked on iOS Oct 7, 2026
…ntinuation of DID-5660)

Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
…ntinuation of DID-5660)

Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
…ntinuation of DID-5660)

Opened by a BotInc run. Delivery follows the workflow's review and merge policy.
@arosasg
arosasg merged commit ba85a4b into main Oct 10, 2026
4 checks passed
@arosasg
arosasg deleted the botinc/run-9f28d4de branch October 10, 2026 07:49
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