Skip to content

iOS: surface SystemIsBusy/SessionTerminatedUnexpectedly and fix UserCanceled#234

Open
Klievan wants to merge 2 commits into
nfcim:developfrom
Klievan:develop
Open

iOS: surface SystemIsBusy/SessionTerminatedUnexpectedly and fix UserCanceled#234
Klievan wants to merge 2 commits into
nfcim:developfrom
Klievan:develop

Conversation

@Klievan
Copy link
Copy Markdown

@Klievan Klievan commented Jun 5, 2026

Problem

On iOS, CoreNFC session-invalidation errors were only delivered to a pending poll. Every in-flight tag operation (transceive, readNDEF, writeNDEF, readBlock, writeBlock, makeNdefReadOnly) used a local result closure and never set self.result, so after a tag was polled:

  • Tapping the cancel button on the bottomsheet hit guard result != nil else { return } in didInvalidateWithError and was silently dropped. The consumer saw the operation's generic 500 comm error instead of UserCanceled.
  • SystemIsBusy and SessionTerminatedUnexpectedly had no mapping and fell through to a generic 500.

Changes

  • mapNFCError: single source of truth mapping CoreNFC errors to HTTP-style codes: 409 UserCanceled, 408 SessionTimeOut, 503 SystemIsBusy, 502 SessionTerminatedUnexpectedly.
  • trackResult: fire-once wrapper; in-flight operations now route through self.result, so didInvalidateWithError completes the pending call exactly once (no hang, no masked 500).
  • Renamed the 409 message SessionCanceledUserCanceled.
  • Example app: new "Session error test (iOS)" section: Stream dummy data keeps the session busy so you can cancel mid-transceive (validates UserCanceled); Test SystemIsBusy drives the real kick-off (502) → instant retry (503) → backoff → recover flow.
  • Docs: README error-code table + CHANGELOG entry.

Codes follow the existing HTTP-style PlatformException convention (no new Dart API). The only behavior change is the 409 message rename (SessionCanceledUserCanceled), noted in the CHANGELOG.

Testing

flutter analyze clean; flutter build ios --no-codesign compiles. Validated on-device: cancel mid-stream returns 409 UserCanceled; the SystemIsBusy flow surfaces 502 on kick-off and 503 on instant retry, recovering after a backoff.

Klievan added 2 commits June 2, 2026 21:49
…anceled

Session-invalidation errors were only delivered to a pending `poll`; any
in-flight tag operation (transceive/readNDEF/writeNDEF/...) used a local
result closure, so when the user canceled after a tag was polled the
`UserCanceled` error was dropped by the `guard result != nil` check — the
Future hung or surfaced the operation's generic 500 instead.

- Add a single `mapNFCError` mapper covering UserCanceled (409),
  SessionTimeOut (408), SystemIsBusy (503) and
  SessionTerminatedUnexpectedly (502); previously the latter two fell
  through to a generic 500.
- Route in-flight operations through a fire-once `trackResult` wrapper so
  `didInvalidateWithError` completes the pending call exactly once.
- Rename the 409 message `SessionCanceled` -> `UserCanceled`.
- Example: add a "Session error test" section (stream dummy data to keep
  the session busy for UserCanceled; drive the kick-off -> instant retry
  -> backoff flow for SystemIsBusy).
- Document the iOS session-error codes in README and CHANGELOG.
iOS: surface SystemIsBusy/SessionTerminatedUnexpectedly and fix UserCanceled
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