Skip to content

React Native: adopt native checkout lifecycle events - #829

Open
markmur wants to merge 7 commits into
rn-checkout-subscription-safetyfrom
rn-checkout-lifecycle-events
Open

markmur wants to merge 7 commits into
rn-checkout-subscription-safetyfrom
rn-checkout-lifecycle-events

Conversation

@markmur

@markmur markmur commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Replace raw protocol callbacks with onStart, onUpdate, onComplete, onFail, and onDismiss for checkout sheets and accelerated buttons. Events expose Kit checkout snapshots through the shared schema-aware decoder, and completion keeps observations alive until dismissal or failure.

Ignore duplicate presentations across ShopifyCheckout instances, preserving the active checkout and its callbacks. Attempts during closing are also ignored; the bridge acknowledges rejected attempts so JS can release their subscriptions without firing consumer callbacks. There is no polling or dismissal timeout.

Cancel pending geolocation responses on dismiss, failure, or teardown, and remove presentation request IDs. Teardown clears consumer callbacks while internal tracking remains until the native session ends. The sample clears completed carts on dismissal or failure.

Pin Swift to 4.0.0-alpha.8 and Android to 4.0.0-alpha.8, update published CocoaPods lockfiles, remove the public protocol handler API, and document the migration. The wrapper guards work with these published SDKs. Link opening uses native defaults; explicit link policy is added in the next PR.

Stack

@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Sep 25, 2026

@markmur markmur left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Three implementation notes for reviewers.

} catch (IOException e) {
Log.e(TAG, "Error emitting link click event", e);
}
return linkAction;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The native SDK needs an immediate link decision, so linkAction is chosen before presentation. onLinkClick is an asynchronous notification; returning a value from JS cannot change the native action.

return;
}
case 'dismiss':
options.onTerminal?.();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Completion keeps the subscription alive while the confirmation page remains visible. Dismissal and failure clean up before invoking app code, allowing those callbacks to safely present another checkout.

}

export function decodeCheckout(value: unknown): Checkout {
return decodeCheckoutSnapshot(value) as unknown as Checkout;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Kit snapshots omit protocol metadata but still need schema-aware decoding. This converts known fields such as line_items to lineItems while preserving arbitrary extension keys and values.

@markmur markmur self-assigned this Sep 25, 2026
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch from 6c54c36 to 5a1262b Compare October 5, 2026 14:18
@markmur
markmur marked this pull request as ready for review October 5, 2026 14:19
@markmur
markmur requested a review from a team as a code owner October 5, 2026 14:19
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Protocol — Coverage Report

Lines Statements Branches Functions
Coverage: 89%
88.26% (158/179) 86.29% (107/124) 85.71% (30/35)

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

React Native — Coverage Report

Lines Statements Branches Functions
Coverage: 95%
94.87% (315/332) 88.73% (197/222) 100% (79/79)

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Web — Coverage Report

Lines Statements Branches Functions
Coverage: 96%
95.16% (374/393) 84.32% (199/236) 97.08% (100/103)

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Bundle Size Budgets

Budget Size Limits Result
React Native npm package (gzip) 114.22 KiB (-6.26 KiB) 130 KiB soft / 150 KiB hard ✅ Within budget

Bundle and package size

Web bundle sizes cover shipped runtime JavaScript. Package sizes cover the full published archive, including any source maps, declarations, and documentation it contains.

Platform Measurement Compression Base Head Delta
React Native npm package (.tgz) gzip 120.5 KiB 114.2 KiB -6.3 KiB
React Native package files (uncompressed)

These are uncompressed file sizes; they do not sum to the compressed package size above.

File Base Head Delta
node_modules/@shopify/checkout-kit-protocol/src/generated/Models.ts 108.7 KiB 108.7 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/Models.d.ts 73.1 KiB 73.1 KiB 0 B
android/src/main/java/com/shopify/reactnative/checkoutkit/ShopifyCheckoutKitModule.java 16.2 KiB 17.7 KiB +1.6 KiB
ios/ShopifyCheckoutKit.swift 16.1 KiB 14.7 KiB -1.3 KiB
ios/AcceleratedCheckoutButtons.swift 14.1 KiB 14.3 KiB +255 B
src/index.ts 13.0 KiB 14.0 KiB +994 B
lib/commonjs/index.js 12.7 KiB 13.5 KiB +777 B
src/components/AcceleratedCheckoutButtons.tsx 13.0 KiB 6.9 KiB -6.1 KiB
lib/module/index.js 10.7 KiB 11.6 KiB +892 B
lib/commonjs/components/AcceleratedCheckoutButtons.js 11.4 KiB 6.8 KiB -4.7 KiB
src/index.d.ts 11.3 KiB 10.7 KiB -622 B
lib/commonjs/components/AcceleratedCheckoutButtons.js.map 10.4 KiB 5.8 KiB -4.6 KiB
lib/module/components/AcceleratedCheckoutButtons.js 10.2 KiB 5.6 KiB -4.7 KiB
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolNotifications.ts 9.5 KiB 9.5 KiB 0 B
lib/module/index.js.map 8.4 KiB 9.4 KiB +936 B
lib/commonjs/index.js.map 8.2 KiB 9.2 KiB +949 B
lib/module/components/AcceleratedCheckoutButtons.js.map 9.1 KiB 4.5 KiB -4.7 KiB
src/present-dispatcher.ts 8.2 KiB 4.0 KiB -4.2 KiB
node_modules/@shopify/checkout-kit-protocol/src/protocol_codec_runtime.ts 7.9 KiB 7.9 KiB 0 B
node_modules/@shopify/checkout-kit-protocol/src/generated/ProtocolRenameMap.ts 7.8 KiB 7.8 KiB 0 B
…and 126 smaller files
How sizes are measured

Measured from the PR base SHA and PR head SHA. Web bundle rows sum shipped .js, .mjs, and .cjs files under dist/, excluding source maps and declarations. The gzip bundle size sums files compressed individually with gzip -n -9. npm package sizes are gzip-compressed .tgz archives; Android AAR sizes are ZIP archives. Package sizes are not final app binary sizes.

@bitrise

bitrise Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Bitrise builds

E2E · iOS CI

Install this build

Open Tophat, select your target device, then click Install. Links open on the Mac running Tophat.

SDK Install
React Native Install with Tophat

Checkout Kit E2E results

Status Tags Target Platform OS version tag Device
✅ launch, checkout-presentation, checkout-completion, buyer-identity react-native ios latest iPhone 15
iOS 27 Beta
✅ launch, checkout-presentation, checkout-completion, buyer-identity react-native android latest Google Pixel 9
Android 17.0

@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch 2 times, most recently from 1ea9ad7 to cb7bb33 Compare October 6, 2026 13:33
@markmur
markmur changed the base branch from main to rn-checkout-subscription-safety October 6, 2026 13:33
@markmur
markmur added this pull request to stack #922 October 6, 2026 13:33
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch from cb7bb33 to eeae337 Compare October 6, 2026 13:38

@tiagocandido tiagocandido 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.

iOS E2E is failing on all four React Native flows (launch, present/close, guest, buyer identity), while Android passes. Do you know if that is related to this change?

): void {
/** Presents checkout with lifecycle callbacks. */
public present(checkoutUrl: string, callbacks?: PresentCallbacks): void {
this.releaseDispatchSubscription();

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.

Is presenting from two ShopifyCheckout instances supported? With a.present() then b.present(), only b's old subscription is removed. a's listener never sees a dismiss after that, so it stays alive forever.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 7369c47. The latest present() owns the single native sheet across instances and removes the previous subscription. A regression test also verifies that tearing down the old instance cannot remove the new owner’s callbacks.

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.

The fix for this seems off to me
In the Android sdk we noop subsequent presents if a checkout is presented

val alreadyPresented = livePresentations[context]?.takeIf { it.isShowing() }
if (alreadyPresented != null) log.w(..., "A checkout is already presented, ignoring this presentation.")
return alreadyPresented?.handle ?: startPresentation(...)

UIKit has this built in effectively so we dont have a direct guard, but UIKit prevents multiple modally presented views on a single vc

Seems like RN goes around this slightly with the way it searches for a vc

I feel like we should add a similar guard to the swift sdk as android and remove this timeout/polling behaviour

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.

i think if we went down that route, and the second present is rejected, we'd be able to remove all the requestId wiring too

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in this update:

  • Added the Swift presentation guard in Ignore duplicate Swift checkout presentations #942.
  • Duplicate RN calls preserve the original checkout and callbacks, including across instances.
  • Removed polling/timeouts and presentation request IDs. Attempts while closing are ignored; an internal bridge acknowledgement releases the rejected JS subscription without firing consumer callbacks.
  • Pending geolocation responses are cancelled on close or teardown, so an old response cannot affect a later checkout.

The RN guards work with the published SDK pins. onDismiss/onFail timing is unchanged, so a present() from those callbacks may be ignored while the sheet is closing. Regression tests cover both platforms, late geolocation responses, and subsequent explicit presentations.

Comment thread platforms/react-native/sample/src/hooks/useCheckoutEventHandlers.ts
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch 2 times, most recently from 7369c47 to 9e0ab13 Compare October 7, 2026 09:42
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch 2 times, most recently from 75ebf71 to 330756f Compare October 8, 2026 10:45
@markmur
markmur removed this pull request from stack #922 October 8, 2026 11:06
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch from 330756f to 462c7c4 Compare October 8, 2026 11:06
@markmur
markmur added this pull request to stack #943 October 8, 2026 11:06
@markmur
markmur removed this pull request from stack #943 October 8, 2026 11:35
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch from 441abf4 to 9719c85 Compare October 8, 2026 11:36
@markmur
markmur added this pull request to stack #947 October 8, 2026 11:36
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch from 9719c85 to 06fd61e Compare October 8, 2026 11:55
@markmur
markmur force-pushed the rn-checkout-lifecycle-events branch 2 times, most recently from 06fd61e to bf5a884 Compare October 8, 2026 12:19

This branch has not been deployed

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

Labels

#gsd:50662 Rebase Checkout Kit on UCP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants