Skip to content

feat: report how an Apple Wallet add finished - #133

Merged
JakubKorytko merged 7 commits into
Expensify:mainfrom
Brunonascdev:feat/ios-add-pass-result
Oct 29, 2026
Merged

JakubKorytko merged 7 commits into
Expensify:mainfrom
Brunonascdev:feat/ios-add-pass-result

Conversation

@Brunonascdev

@Brunonascdev Brunonascdev commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Details

addCardToAppleWallet used to treat every finish the same. A successful add, a cancel after the issuer step, and a Wallet decline all collapsed, and a decline rejected with "Could not add card" even when Wallet had already shown its own message.

didFinishAdding now resolves a dictionary instead of rejecting those cases. The promise resolves IOSAddCardResult:

  • status is success, canceled, or error
  • success includes activationState (requireActivation, active, and the other CardStatus values) when PassKit reports a known state. An unknown state is left out, so the add still resolves success
  • a user cancel (PKAddPaymentPassError.userCancelled) resolves canceled
  • any other PassKit error resolves error and includes errorDomain and errorCode

status uses OperationResult raw values, including a new declined = -1 case, and getTokenizationStatus maps them. Try again is unchanged: a response that still has a nonce calls the issuer callback again.

This is a breaking change. Callers read result.status instead of a TokenizationStatus. A decline resolves { status: 'error', errorDomain, errorCode } instead of rejecting. A cancel after the issuer step resolves { status: 'canceled' } instead of rejecting. An error thrown by the issuer callback still rejects.

The existing bridge already forwards that dictionary, so RNWallet.mm and the Android stubs are unchanged.

Related Issues

No existing issue. The end states showed up while adding a card from an app that is on TestFlight.

Manual Tests

This repo has no automated tests. Typecheck and lint pass. The example app builds for the iOS simulator with the new architecture.

  • Add a card that lands in verification. The result is { status: 'success', activationState: 'requireActivation' }.
  • Cancel after the issuer step. The result is { status: 'canceled' }.
  • Decline in a way Wallet already explains. The result is { status: 'error', errorDomain, errorCode }, and there is no second error.
  • Try again still re-enters the issuer callback.

Linked PRs

Merged current main, which includes #91, #96, #132, and #134. The issuer-error path still closes the sheet.

A pass waiting on verification, a cancel, and a decline Wallet already explained now come back as a result instead of one rejection.
@Brunonascdev

Copy link
Copy Markdown
Contributor Author

The other splits of the same TestFlight patch are #132 and #134. #91 and #96 are the existing ones. They share WalletManager.swift and src/index.tsx, so whichever lands second needs a small merge.

@Brunonascdev

Copy link
Copy Markdown
Contributor Author

@JakubKorytko for a look when you have a minute. Requesting a review from the fork returns a permissions error, so this comment is the ping.

@JakubKorytko JakubKorytko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

a few things on the result shape and status values + conflicts

Comment thread src/index.tsx Resolved
Comment thread src/index.tsx Resolved
Comment thread src/index.tsx Resolved
Comment thread ios/WalletManager.swift Resolved
The outcome comes back on the resolved value, including a decline Wallet already explained, instead of a second callback.
@Brunonascdev

Copy link
Copy Markdown
Contributor Author

@JakubKorytko merged current main and addressed the review.

@Brunonascdev

Copy link
Copy Markdown
Contributor Author

@JakubKorytko merged current main.

@JakubKorytko

Copy link
Copy Markdown
Member

@Brunonascdev main again :/

@Brunonascdev

Copy link
Copy Markdown
Contributor Author

@JakubKorytko merged current main, including #132. The issuer-error path still closes the sheet, and the promise still resolves IOSAddCardResult otherwise.

@Brunonascdev

Copy link
Copy Markdown
Contributor Author

@JakubKorytko merged current main, including #134. The README keeps presentAppleWalletPass and the IOSAddCardResult return.

@JakubKorytko

Copy link
Copy Markdown
Member

@Brunonascdev thanks! One last thing: since the return type changes and a decline/cancel no longer rejects, could you set the version in package.json to 0.3.0-0? The publish workflow bumps patch, so it ships as 0.3.0 and ^0.2 users don't pick up the breaking change automatically. Same approach as the UAPP change.

@Brunonascdev

Copy link
Copy Markdown
Contributor Author

@JakubKorytko set the version to 0.3.0-0, same as the UAPP bump. The publish workflow's patch bump will ship this as 0.3.0.

@JakubKorytko JakubKorytko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks a lot!

@JakubKorytko
JakubKorytko merged commit 0bb9dd6 into Expensify:main Oct 29, 2026
7 of 8 checks passed
@os-botify

os-botify Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

🚀 Published to npm in 0.3.0 🎉

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.

2 participants