Skip to content

Wire Swift kit for UCP events#65

Open
markmur wants to merge 16 commits into
mainfrom
swift/remove-delegations
Open

Wire Swift kit for UCP events#65
markmur wants to merge 16 commits into
mainfrom
swift/remove-delegations

Conversation

@markmur
Copy link
Copy Markdown
Contributor

@markmur markmur commented May 12, 2026

What changes are you making?

Implements UCP bridge for 2026-04-08 in Swift kit + sample app.

Important

  1. Does not use ?branding param. Uses ec_color_scheme for now
  2. The ShopifyCheckoutProtocol is packaged in the ShopifyCheckoutKit cocoapod.
  3. This will merge without the window.open delegation and be handled in a fast fallow.
  4. User-Agent is not final. Will be handled in follow up

Checklist

Before you merge

[!IMPORTANT]

  • I've added tests to support my implementation
  • I have read and agree with the Contribution Guidelines
  • I have read and agree with the Code of Conduct
  • I've updated the relevant platform README (platforms/swift/README.md and/or platforms/android/README.md)

Releasing a new Swift version?
  • I have bumped the version in platforms/swift/ShopifyCheckoutKit.podspec
  • I have bumped the version in platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift
  • I have updated platforms/swift/CHANGELOG.md
  • I have updated the SwiftPM/CocoaPods version snippets in platforms/swift/README.md (major version only)
Releasing a new Android version?
  • I have bumped the versionName in platforms/android/lib/build.gradle
  • I have updated platforms/android/CHANGELOG.md
  • I have updated the Gradle/Maven version snippets in platforms/android/README.md

[!TIP]
See the Contributing documentation for the full release process per platform.

@markmur markmur force-pushed the swift/remove-delegations branch 3 times, most recently from 759d198 to f75bc63 Compare May 12, 2026 10:24
@markmur markmur changed the title Move ec param decoration to kit Wire up ec_* params for UCP May 12, 2026
@markmur markmur force-pushed the swift/remove-delegations branch from 38999c3 to 8f4ff5b Compare May 12, 2026 12:05
@shopify-bumperbot
Copy link
Copy Markdown

🔍 Denylist Analysis Results

Analyzed 1 packages.

.github/workflows/swift-test-protocol.yml


Known Packages - Approved for Use (1)
Package Version Details
actions/checkout de0fac2e4500dabe0009e67214ff5f5447ce83dd Package policy allows this version

⏳ Package Age Warnings

Packages With Unknown Age (1) — unable to determine publish date
Package Version Ecosystem
actions/checkout de0fac2e4500dabe0009e67214ff5f5447ce83dd githubactions

For any questions or to provide feedback, please reach out to #help-bumperbot.

Generated by Bumperbot.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 12, 2026

React Native — Coverage Report

Lines Statements Branches Functions
Coverage: 99%
99.53% (214/215) 95.96% (119/124) 100% (64/64)

@markmur markmur force-pushed the swift/remove-delegations branch from bba6c9c to a1043aa Compare May 12, 2026 12:57
@markmur markmur self-assigned this May 12, 2026
@markmur markmur added #gsd:50662 Rebase Checkout Kit on UCP labels May 12, 2026
@markmur markmur changed the title Wire up ec_* params for UCP Wire Swift kit for UCP events May 12, 2026
@markmur markmur marked this pull request as ready for review May 12, 2026 13:14
@markmur markmur requested a review from a team as a code owner May 12, 2026 13:14
@markmur markmur force-pushed the swift/remove-delegations branch from f85ca58 to 5db6643 Compare May 12, 2026 15:55
@markmur markmur requested a review from a team as a code owner May 12, 2026 15:55
Comment on lines +24 to +26
#if !COCOAPODS
import ShopifyCheckoutProtocol
#endif
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 protocol gets bundled as a source in the cocoapod

}

private static let baseUserAgent = "ShopifyCheckoutSDK/\(MetaData.version)"
private static let baseUserAgent = "ShopifyCheckoutKit/\(MetaData.version)"
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.

This is not final. It can be handled in a follow up either for swift specifically or across platforms in the repo

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.

Comment on lines +28 to +35
public static let complete = NotificationDescriptor<Checkout>(method: "ec.complete")
public static let error = NotificationDescriptor<ErrorResponse>(method: "ec.error")
public static let lineItemsChange = NotificationDescriptor<Checkout>(
method: "ec.line_items.change")
public static let messagesChange = NotificationDescriptor<Checkout>(
method: "ec.messages.change")
public static let start = NotificationDescriptor<Checkout>(method: "ec.start")
public static let totalsChange = NotificationDescriptor<Checkout>(method: "ec.totals.change")
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.

Verify all of the events here look as expected.

The window.open delegation will be handled in a follow up

Comment on lines -101 to -134
@Test func delegationsReturnsRegisteredDelegationStrings() {
let client = CheckoutProtocol.Client()
.on(CheckoutProtocol.instrumentsChangeRequest) { (_: Checkout) in
InstrumentsChangeResult(
checkout: InstrumentsChangeCheckout(
payment: InstrumentsChangePayment(instruments: nil, selectedInstrumentID: nil)
)
)
}
.on(CheckoutProtocol.credentialRequest) { (_: Checkout) in
CredentialResult(
checkout: CredentialCheckout(
payment: CredentialPayment(instruments: nil)
)
)
}

#expect(client.delegations.sorted() == ["payment.credential", "payment.instruments_change"])
}

@Test func builderChainingCompiles() {
let client = CheckoutProtocol.Client()
.on(CheckoutProtocol.start) { (_: Checkout) in }
.on(CheckoutProtocol.complete) { (_: Checkout) in }
.on(CheckoutProtocol.credentialRequest) { (_: Checkout) in
CredentialResult(
checkout: CredentialCheckout(
payment: CredentialPayment(instruments: nil)
)
)
}

#expect(client.delegations.count == 1)
}
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.

These are all based on unused delegations on an old version. Deleting so we can implement window.open fresh from 2026-04-08

markmur added 9 commits May 12, 2026 21:28
CocoaPods silently drops source_files paths outside the spec dir, so
'../../protocol/languages/swift/...' globs were not bundled and 'pod
lib lint' failed with 6 'cannot find CheckoutProtocol in scope' errors.
Move the Swift protocol sources physically under
platforms/swift/Sources/ShopifyCheckoutProtocol and replace the SPM
location with a symlink so the SPM target path stays inside its
package root.

The earlier sed rename in generate_models.sh anchored on a trailing
space ('struct Binding ') but quicktype emits 'struct Binding:' with
no whitespace, so the rename never fired and the SwiftUI sample app
failed with 'Binding is ambiguous'. Switch to BSD word-boundary
anchors so all identifier sites get rewritten, and regenerate
Models.swift.
Keep one copy of the Swift protocol sources under
protocol/languages/swift/Sources/ShopifyCheckoutProtocol (their
conceptual home, alongside the schemas and other language ports) and
expose them inside the podspec dir via a symlink at
platforms/swift/Sources/ShopifyCheckoutProtocol. CocoaPods' source_files
glob follows the symlink, so the Core subspec still compiles them.

Point the generator OUTPUT back at the canonical path.
@markmur markmur force-pushed the swift/remove-delegations branch from 3bd0ec8 to effab32 Compare May 12, 2026 20:28
Comment on lines +252 to +253
if let response = CheckoutProtocol.acknowledgeReady(body) {
checkoutBridge.sendResponse(self, messageBody: response)
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.

This is where the ec.ready response happens

Comment on lines +24 to +28
protocol MutableCopyable {
func copy(_ mutate: (inout Self) -> Void) -> Self
}

extension Copyable {
extension MutableCopyable {
Copy link
Copy Markdown
Contributor Author

@markmur markmur May 12, 2026

Choose a reason for hiding this comment

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

Was running into conflicts with other Copyable protocols in the repo. We can likely merge them all

kieran-osgood-shopify added a commit that referenced this pull request May 13, 2026
… in place

Reverts the file move from PR #65 so ShopifyCheckoutProtocol stays under
protocol/languages/swift/, and instead moves ShopifyCheckoutKit.podspec from
platforms/swift/ to the repository root. With the podspec at the root, both
source trees are 'inside' the pod's working directory and can be globbed
without using `..` paths:

    core.source_files = [
      'platforms/swift/Sources/ShopifyCheckoutKit/**/*.swift',
      'protocol/languages/swift/Sources/ShopifyCheckoutProtocol/**/*.swift',
    ]

Other changes:
  * License path simplified from '../../LICENSE' to 'LICENSE'.
  * AcceleratedCheckouts subspec source/resource paths re-rooted similarly.
  * Updated docs that referenced the old podspec path
    (.github/CONTRIBUTING.md, .github/pull_request_template.md).
  * Restored protocol/languages/swift/{Package.swift,README.md,.gitignore}.
  * Root Package.swift target paths point back at protocol/languages/swift.
  * #if !COCOAPODS guards in ShopifyCheckoutKit are kept (under CocoaPods both
    targets compile into one module, so the import would be unresolvable).

RESULT: works.

  bundle exec pod lib lint ShopifyCheckoutKit.podspec --allow-warnings
    -> ShopifyCheckoutKit passed validation.

Both subspecs (Core and AcceleratedCheckouts) build cleanly. The only
diagnostics are the pre-existing JSONAny Sendable warnings in the generated
Models.swift, identical to PR #65's baseline.

Lint must be invoked from the repo root, e.g.:
  BUNDLE_GEMFILE=platforms/swift/Gemfile bundle exec pod lib lint \
    ShopifyCheckoutKit.podspec --allow-warnings

Trade-offs vs PR #65:
  + No duplicated source tree; protocol files live in one place.
  + No #if !COCOAPODS source-import guards needed beyond what PR #65 already
    added (kept for safety).
  - Podspec lives at the repo root, slightly unusual for a multi-platform
    monorepo. CONTRIBUTING.md / PR template updated to reflect the new path.
  - The Gemfile remains under platforms/swift/, so contributors need to set
    BUNDLE_GEMFILE when running pod commands from the root (or we move the
    Gemfile too in a follow-up).
kieran-osgood-shopify added a commit that referenced this pull request May 13, 2026
…he kit

Reverts the file move from PR #65 so ShopifyCheckoutProtocol stays under
protocol/languages/swift/, restores its standalone Package.swift, and adds a
new sibling cocoapod:

  protocol/languages/swift/ShopifyCheckoutProtocol.podspec

ShopifyCheckoutKit.podspec then declares a regular pod dependency:

    core.dependency 'ShopifyCheckoutProtocol', "= #{s.version}"

This mirrors the SwiftPM target split exactly: in both worlds (SwiftPM and
CocoaPods) ShopifyCheckoutKit is a separate module that imports
ShopifyCheckoutProtocol, so the `#if !COCOAPODS` shims that PR #65 added
around `import ShopifyCheckoutProtocol` can be removed (done here in
CheckoutViewController.swift, CheckoutWebView.swift, ShopifyCheckoutKit.swift).

RESULT: works.

  # Lint the protocol pod first
  cd protocol/languages/swift
  BUNDLE_GEMFILE=../../../platforms/swift/Gemfile bundle exec pod lib lint \
    ShopifyCheckoutProtocol.podspec --allow-warnings
    -> ShopifyCheckoutProtocol passed validation.

  # Then the kit pod, told where to find the local protocol pod
  cd platforms/swift
  BUNDLE_GEMFILE=Gemfile bundle exec pod lib lint ShopifyCheckoutKit.podspec \
    --allow-warnings \
    --include-podspecs=../../protocol/languages/swift/ShopifyCheckoutProtocol.podspec
    -> ShopifyCheckoutKit passed validation.

Both Core and AcceleratedCheckouts subspecs build cleanly. Same JSONAny
Sendable warnings as the baseline.

Trade-offs vs PR #65:
  + Single source of truth: protocol files live in protocol/languages/swift,
    reachable by Swift consumers (SwiftPM standalone Package, the umbrella
    Package, CocoaPods, and Android via the protocol/ scripts).
  + No source-import #if shims; the module exists in both worlds.
  + ShopifyCheckoutProtocol becomes independently consumable by other Swift
    apps that want UCP types without pulling in the WebView kit.
  - Two pods to publish and version. Release flow needs to push
    ShopifyCheckoutProtocol to trunk before ShopifyCheckoutKit (and they
    must share s.version, which is enforced via `= #{s.version}` in the
    dependency declaration).
  - CONTRIBUTING.md release docs need a follow-up note about pushing both
    podspecs (not done in this commit; intentionally left for a follow-up
    once we settle on the approach).

Note: pod lib lint of ShopifyCheckoutKit requires --include-podspecs because
the protocol pod has not been published to trunk yet. Once published, that
flag is no longer needed.
}
},
{
"identity" : "viewinspector",
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.

was the intentional?

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.

2 participants