Add decode logging parity for Web + Swift + Android#437
Merged
Conversation
Package Size
Web file breakdown
React Native file breakdown
Android file breakdown
Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report. |
Contributor
Author
11 tasks
kieran-osgood-shopify
requested changes
Jul 13, 2026
markmur
force-pushed
the
decode-error-logging-parity
branch
from
July 13, 2026 16:09
48cc8eb to
6e6776f
Compare
kieran-osgood-shopify
approved these changes
Jul 13, 2026
markmur
force-pushed
the
decode-error-logging-parity
branch
from
July 13, 2026 17:08
6e6776f to
296e415
Compare
This was referenced Jul 13, 2026
markmur
force-pushed
the
decode-error-logging-parity
branch
from
July 14, 2026 08:22
296e415 to
0070c87
Compare
markmur
added a commit
that referenced
this pull request
Jul 14, 2026
### What changes are you making? Kotlin was the odd platform out: TS (`client.ts`) and Swift (`EmbeddedCheckoutProtocol+Client.swift`) ship a generic dispatch `Client` — with `onDecodeError` — inside their protocol packages, but Kotlin had no `Client` in the `embedded-checkout-protocol` artifact. The dispatch loop lived entirely in the kit's `CheckoutProtocol.Client`, which decoded, routed, and encoded inline. This PR closes that structural gap and exposes `onDecodeError` publicly, matching the parity #437 established for TS + Swift. Stacked on #437. **Protocol (`embedded-checkout-protocol`)** - New generic `Client`: `on(NotificationDescriptor)`, `on(RequestDescriptor)`, chainable `onDecodeError`, and `process(message)`. It is thread-agnostic and synchronous — handlers run on the calling thread, so hosts wrap them for threading before registering. - `onDecodeError` is `public` (Kotlin lands at TS's public level; it has no `package` visibility and `internal` in the artifact is invisible to the kit). A `SerializationException` while decoding a notification skips dispatch and fires the callback; a request with undecodable params fires the callback and answers with an invalid-params error; envelope-level failures stay silent (matching the `.unknown` path on the other platforms). - `hasValidJsonRpcRequestId` moved into the artifact codec (now shared/public) since both the generic client and the kit need it. **Kit (`checkout-kit`)** - `CheckoutProtocol.Client` is now a thin wrapper over the generic client: it keeps Checkout Kit curation (only supported descriptors register), main-thread delivery of consumer handlers, and wires `onDecodeError` to `log.e` once. The duplicated decode/route/encode/catch code is gone. Public kit API is unchanged (`lib.api` untouched). The `embedded-checkout-protocol.api` baseline gains `Client` and `hasValidJsonRpcRequestId`; that diff is intended. ### Usage **Directly against the protocol artifact** (`com.shopify:embedded-checkout-protocol`) — for a custom transport/WebView integration: ```kotlin import com.shopify.ucp.embedded.checkout.Client import com.shopify.ucp.embedded.checkout.EmbeddedCheckoutProtocol val client = Client() .onDecodeError { method, error -> Log.e("MyApp", "Failed to decode $method", error) // you wire your own logging } .on(EmbeddedCheckoutProtocol.start) { params -> // raw protocol payload — you unwrap it yourself; runs on the calling thread println("started: ${params.checkout.id}") } .on(EmbeddedCheckoutProtocol.windowOpen) { request -> WindowOpenResult(/* … */) // request handler returns the response } // you drive it from your own message pump; response is the JSON-RPC reply (or null) val response: String? = client.process(rawJsonRpcMessage) ``` **Through Checkout Kit** (`com.shopify:checkout-kit`) — the managed path, wired into the kit's WebView automatically: ```kotlin import com.shopify.checkoutkit.CheckoutProtocol val client = CheckoutProtocol.Client() .on(CheckoutProtocol.start) { checkout -> // payload already unwrapped to `Checkout`, delivered on the main thread println("started: ${checkout.id}") } .on(CheckoutProtocol.windowOpen) { request -> WindowOpenResult(/* … */) } // no onDecodeError needed — the kit wires it to its logger; no process() — the WebView bridge drives it ``` The kit adds, on top of the generic client: payload unwrapping (`Checkout` vs raw `CheckoutParams`), descriptor curation (only supported events register), main-thread handler delivery, decode-error logging, and WebView wiring. The protocol client is the unopinionated primitive underneath. --- ### Before you merge > [!IMPORTANT] > > - [x] I've added tests to support my implementation > - [x] I have read and agree with the [Contribution Guidelines](./CONTRIBUTING.md) > - [x] I have read and agree with the [Code of Conduct](./CODE_OF_CONDUCT.md) > - [ ] I've updated the relevant platform README (`platforms/swift/README.md` and/or `platforms/android/README.md`) --- <details> <summary>Releasing a new Swift version?</summary> - [ ] I have bumped the version in `ShopifyCheckoutKit.podspec` - [ ] I have bumped the version in `platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift` - [ ] I have updated the SwiftPM/CocoaPods version snippets in `platforms/swift/README.md` (major version only) </details> <details> <summary>Releasing a new Embedded Checkout Protocol version?</summary> - [ ] I have bumped `embeddedCheckoutProtocolAndroid` in `platforms/android/gradle/libs.versions.toml` - [ ] I have updated `protocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.api` if the public API changed </details> <details> <summary>Releasing a new Android version?</summary> - [ ] I have bumped `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml` - [ ] I have updated the Gradle/Maven version snippets in `platforms/android/README.md` </details> > [!TIP] > See the [Contributing documentation](./CONTRIBUTING.md) for the full release process per platform.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What changes are you making?
A payload that fails to decode was dropped silently on Web and Swift, and only at debug level on Android (suppressed at the default
WARN) — so merchants lost the event with nothing to debug. This brings all three platforms to parity: a dropped payload now always leaves a log line.Adds a chainable,
publiconDecodeErrorcallback to the shared protocol client (TS + Swift) that each kit wires to its own logger. Android already has direct logger access in the kit, so it logs inline instead.Client.onDecodeError(handler)+DecodeErrorContext; fired on both notification and request decode failures.decodenowthrows(wastry?-swallowed tonil), so the realDecodingErrorsurfaces; newpublic onDecodeErrorcaptures it.#warn(prefix<shopify-checkout>:).OSLogger.shared.error(...).log.d→log.e(unconditional). Envelope-level failures stay at debug, matching the silent.unknownpath elsewhere.checkout-kitpublic API is unchanged. SwiftEmbeddedCheckoutProtocolbaseline changes are intended:decodeparam(Data) -> Payload?→(Data) throws -> Payload, andonDecodeError/DecodeErrorHandlernowpublic. First PR in a stack improving decode tolerance.Before you merge
Important
platforms/swift/README.mdand/orplatforms/android/README.md)