Skip to content

refactor: move EventHandlers to CheckoutSheetKit and simplify event handling #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import Apollo
import ShopifyAcceleratedCheckouts
import ShopifyCheckoutSheetKit
import SwiftUI

struct ButtonSet: View {
Expand All @@ -35,11 +36,12 @@ struct ButtonSet: View {
CheckoutSection(title: "AcceleratedCheckoutButtons(cartID:)") {
// Cart-based checkout example with event handlers
AcceleratedCheckoutButtons(cartID: cartID)
.onComplete {
.onComplete { event in
print("✅ Checkout completed successfully")
print(" Order ID: \(event.orderDetails.id)")
}
.onFail {
print("❌ Checkout failed")
.onFail { error in
print("❌ Checkout failed: \(error)")
}
.onCancel {
print("🚫 Checkout cancelled")
Expand All @@ -52,8 +54,16 @@ struct ButtonSet: View {
.onClickLink { url in
print("🔗 Link clicked: \(url)")
}
.onWebPixelEvent { _ in
print("📊 Web pixel event received")
.onWebPixelEvent { event in
let eventName: String = {
switch event {
case let .customEvent(customEvent):
return customEvent.name ?? "Unknown custom event"
case let .standardEvent(standardEvent):
return standardEvent.name ?? "Unknown standard event"
}
}()
print("📊 Web pixel event: \(eventName)")
}
}
}
Expand All @@ -69,11 +79,12 @@ struct ButtonSet: View {
)
.cornerRadius(24)
.withWallets([.applepay, .shoppay])
.onComplete {
.onComplete { event in
print("✅ Variant checkout completed")
print(" Order ID: \(event.orderDetails.id)")
}
.onFail {
print("❌ Variant checkout failed")
.onFail { error in
print("❌ Variant checkout failed: \(error)")
}
.onCancel {
print("🚫 Variant checkout cancelled")
Expand All @@ -85,8 +96,16 @@ struct ButtonSet: View {
.onClickLink { url in
print("🔗 Variant - Link clicked: \(url)")
}
.onWebPixelEvent { _ in
print("📊 Variant - Web pixel event received")
.onWebPixelEvent { event in
let eventName: String = {
switch event {
case let .customEvent(customEvent):
return customEvent.name ?? "Unknown custom event"
case let .standardEvent(standardEvent):
return standardEvent.name ?? "Unknown standard event"
}
}()
print("📊 Variant - Web pixel event: \(eventName)")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,15 @@ extension AcceleratedCheckoutButtons {
///
/// ```swift
/// AcceleratedCheckoutButtons(cartID: cartId)
/// .onComplete {
/// // Navigate to success screen
/// showSuccessView = true
/// .onComplete { event in
/// // Navigate to success screen with order ID
/// showSuccessView(orderId: event.orderId)
/// }
/// ```
///
/// - Parameter action: The action to perform when checkout succeeds
/// - Returns: A view with the checkout success handler set
public func onComplete(_ action: @escaping () -> Void) -> AcceleratedCheckoutButtons {
public func onComplete(_ action: @escaping (CheckoutCompletedEvent) -> Void) -> AcceleratedCheckoutButtons {
var newView = self
newView.eventHandlers.checkoutDidComplete = action
return newView
Expand All @@ -156,15 +156,15 @@ extension AcceleratedCheckoutButtons {
///
/// ```swift
/// AcceleratedCheckoutButtons(cartID: cartId)
/// .onFail {
/// // Show error alert
/// showErrorAlert = true
/// .onFail { error in
/// // Show error alert with details
/// showErrorAlert(error: error)
/// }
/// ```
///
/// - Parameter action: The action to perform when checkout fails
/// - Returns: A view with the checkout error handler set
public func onFail(_ action: @escaping () -> Void) -> AcceleratedCheckoutButtons {
public func onFail(_ action: @escaping (CheckoutError) -> Void) -> AcceleratedCheckoutButtons {
var newView = self
newView.eventHandlers.checkoutDidFail = action
return newView
Expand Down Expand Up @@ -205,7 +205,7 @@ extension AcceleratedCheckoutButtons {
/// - Parameter action: The action to determine if recovery should be attempted
/// - Returns: A view with the error recovery handler set
public func onShouldRecoverFromError(
_ action: @escaping (ShopifyCheckoutSheetKit.CheckoutError) -> Bool
_ action: @escaping (CheckoutError) -> Bool
) -> AcceleratedCheckoutButtons {
var newView = self
newView.eventHandlers.shouldRecoverFromError = action
Expand Down Expand Up @@ -246,7 +246,7 @@ extension AcceleratedCheckoutButtons {
///
/// - Parameter action: The action to perform when a pixel event is emitted
/// - Returns: A view with the web pixel event handler set
public func onWebPixelEvent(_ action: @escaping (ShopifyCheckoutSheetKit.PixelEvent) -> Void)
public func onWebPixelEvent(_ action: @escaping (PixelEvent) -> Void)
-> AcceleratedCheckoutButtons
{
var newView = self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ struct Internal_ApplePayButton: View {
self.label = label
self.cornerRadius = cornerRadius
MainActor.assumeIsolated {
controller.onComplete = eventHandlers.checkoutDidComplete
controller.onFail = eventHandlers.checkoutDidFail
controller.onCancel = eventHandlers.checkoutDidCancel
controller.onCheckoutComplete = eventHandlers.checkoutDidComplete
controller.onCheckoutFail = eventHandlers.checkoutDidFail
controller.onCheckoutCancel = eventHandlers.checkoutDidCancel
controller.onShouldRecoverFromError = eventHandlers.shouldRecoverFromError
controller.onClickLink = eventHandlers.checkoutDidClickLink
controller.onWebPixelEvent = eventHandlers.checkoutDidEmitWebPixelEvent
controller.onCheckoutClickLink = eventHandlers.checkoutDidClickLink
controller.onCheckoutWebPixelEvent = eventHandlers.checkoutDidEmitWebPixelEvent
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,39 +54,39 @@ protocol PayController: AnyObject {
///
/// Example usage:
/// ```swift
/// applePayViewController.onComplete = { [weak self] in
/// applePayViewController.onCheckoutComplete = { [weak self] event in
/// self?.presentSuccessScreen()
/// self?.logAnalyticsEvent(.checkoutCompleted)
/// self?.logAnalyticsEvent(.checkoutCompleted, orderId: event.orderId)
/// }
/// ```
@MainActor
public var onComplete: (() -> Void)?
public var onCheckoutComplete: ((CheckoutCompletedEvent) -> Void)?

/// Callback invoked when an error occurs during the checkout process.
/// This closure is called on the main thread when the payment fails or is cancelled.
/// This closure is called on the main thread when the payment fails.
///
/// Example usage:
/// ```swift
/// applePayViewController.onFail = { [weak self] in
/// self?.showErrorAlert()
/// self?.logAnalyticsEvent(.checkoutFailed)
/// applePayViewController.onCheckoutFail = { [weak self] error in
/// self?.showErrorAlert(for: error)
/// self?.logAnalyticsEvent(.checkoutFailed, error: error)
/// }
/// ```
@MainActor
public var onFail: (() -> Void)?
public var onCheckoutFail: ((CheckoutError) -> Void)?

/// Callback invoked when the checkout process is cancelled by the user.
/// This closure is called on the main thread when the user dismisses the checkout.
///
/// Example usage:
/// ```swift
/// applePayViewController.onCancel = { [weak self] in
/// applePayViewController.onCheckoutCancel = { [weak self] in
/// self?.resetCheckoutState()
/// self?.logAnalyticsEvent(.checkoutCancelled)
/// }
/// ```
@MainActor
public var onCancel: (() -> Void)?
public var onCheckoutCancel: (() -> Void)?

/// Callback invoked to determine if checkout should recover from an error.
/// This closure is called on the main thread when an error occurs.
Expand All @@ -100,33 +100,33 @@ protocol PayController: AnyObject {
/// }
/// ```
@MainActor
public var onShouldRecoverFromError: ((ShopifyCheckoutSheetKit.CheckoutError) -> Bool)?
public var onShouldRecoverFromError: ((CheckoutError) -> Bool)?

/// Callback invoked when the user clicks a link during checkout.
/// This closure is called on the main thread when a link is clicked.
///
/// Example usage:
/// ```swift
/// applePayViewController.onClickLink = { [weak self] url in
/// applePayViewController.onCheckoutClickLink = { [weak self] url in
/// self?.handleExternalLink(url)
/// self?.logAnalyticsEvent(.linkClicked, url: url)
/// }
/// ```
@MainActor
public var onClickLink: ((URL) -> Void)?
public var onCheckoutClickLink: ((URL) -> Void)?

/// Callback invoked when a web pixel event is emitted during checkout.
/// This closure is called on the main thread when pixel events occur.
///
/// Example usage:
/// ```swift
/// applePayViewController.onWebPixelEvent = { [weak self] event in
/// applePayViewController.onCheckoutWebPixelEvent = { [weak self] event in
/// self?.trackPixelEvent(event)
/// self?.logAnalyticsEvent(.pixelFired, event: event)
/// }
/// ```
@MainActor
public var onWebPixelEvent: ((ShopifyCheckoutSheetKit.PixelEvent) -> Void)?
public var onCheckoutWebPixelEvent: ((PixelEvent) -> Void)?

/// Initialization workaround for passing self to ApplePayAuthorizationDelegate
private var __authorizationDelegate: ApplePayAuthorizationDelegate!
Expand Down Expand Up @@ -186,7 +186,9 @@ protocol PayController: AnyObject {
return try await handleStorefrontError(error)
} catch {
await authorizationDelegate.transition(to: .terminalError(error: error))
await onFail?()
if let checkoutError = error as? CheckoutError {
await onCheckoutFail?(checkoutError)
}
throw error
}
}
Expand Down Expand Up @@ -241,30 +243,41 @@ protocol PayController: AnyObject {

@available(iOS 17.0, *)
extension ApplePayViewController: CheckoutDelegate {
@MainActor func checkoutDidComplete(event _: ShopifyCheckoutSheetKit.CheckoutCompletedEvent) {
onComplete?()
func checkoutDidComplete(event: CheckoutCompletedEvent) {
Task { @MainActor in
self.onCheckoutComplete?(event)
}
}

@MainActor func checkoutDidFail(error _: ShopifyCheckoutSheetKit.CheckoutError) {
onFail?()
func checkoutDidFail(error: CheckoutError) {
Task { @MainActor in
self.onCheckoutFail?(error)
}
}

@MainActor func checkoutDidCancel() {
/// x right button on CSK doesn't dismiss automatically
checkoutViewController?.dismiss(animated: true)

onCancel?()
func checkoutDidCancel() {
Task { @MainActor in
/// x right button on CSK doesn't dismiss automatically
checkoutViewController?.dismiss(animated: true)
self.onCheckoutCancel?()
}
}

@MainActor func shouldRecoverFromError(error: ShopifyCheckoutSheetKit.CheckoutError) -> Bool {
return onShouldRecoverFromError?(error) ?? false
func shouldRecoverFromError(error: CheckoutError) -> Bool {
return MainActor.assumeIsolated {
self.onShouldRecoverFromError?(error) ?? false
}
}

@MainActor func checkoutDidClickLink(url: URL) {
onClickLink?(url)
func checkoutDidClickLink(url: URL) {
Task { @MainActor in
self.onCheckoutClickLink?(url)
}
}

@MainActor func checkoutDidEmitWebPixelEvent(event: ShopifyCheckoutSheetKit.PixelEvent) {
onWebPixelEvent?(event)
func checkoutDidEmitWebPixelEvent(event: PixelEvent) {
Task { @MainActor in
self.onCheckoutWebPixelEvent?(event)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ import SwiftUI

@available(iOS 17.0, *)
extension ShopPayViewController: CheckoutDelegate {
func checkoutDidComplete(event _: ShopifyCheckoutSheetKit.CheckoutCompletedEvent) {
eventHandlers.checkoutDidComplete?()
func checkoutDidComplete(event: CheckoutCompletedEvent) {
eventHandlers.checkoutDidComplete?(event)
}

func checkoutDidFail(error _: ShopifyCheckoutSheetKit.CheckoutError) {
func checkoutDidFail(error: CheckoutError) {
checkoutViewController?.dismiss(animated: true)
eventHandlers.checkoutDidFail?()
eventHandlers.checkoutDidFail?(error)
}

func checkoutDidCancel() {
Expand All @@ -122,15 +122,15 @@ extension ShopPayViewController: CheckoutDelegate {
eventHandlers.checkoutDidCancel?()
}

func checkoutShouldRecoverFromError(error: ShopifyCheckoutSheetKit.CheckoutError) -> Bool {
func shouldRecoverFromError(error: CheckoutError) -> Bool {
return eventHandlers.shouldRecoverFromError?(error) ?? false
}

func checkoutDidClickLink(url: URL) {
eventHandlers.checkoutDidClickLink?(url)
}

func checkoutDidEmitWebPixelEvent(event: ShopifyCheckoutSheetKit.PixelEvent) {
func checkoutDidEmitWebPixelEvent(event: PixelEvent) {
eventHandlers.checkoutDidEmitWebPixelEvent?(event)
}
}
26 changes: 0 additions & 26 deletions Sources/ShopifyAcceleratedCheckouts/Wallets/Wallet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,6 @@ public enum Wallet {
case shoppay
}

/// Event handlers for wallet buttons
public struct EventHandlers {
public var checkoutDidComplete: (() -> Void)?
public var checkoutDidFail: (() -> Void)?
public var checkoutDidCancel: (() -> Void)?
public var shouldRecoverFromError: ((ShopifyCheckoutSheetKit.CheckoutError) -> Bool)?
public var checkoutDidClickLink: ((URL) -> Void)?
public var checkoutDidEmitWebPixelEvent: ((ShopifyCheckoutSheetKit.PixelEvent) -> Void)?

public init(
checkoutDidComplete: (() -> Void)? = nil,
checkoutDidFail: (() -> Void)? = nil,
checkoutDidCancel: (() -> Void)? = nil,
shouldRecoverFromError: ((ShopifyCheckoutSheetKit.CheckoutError) -> Bool)? = nil,
checkoutDidClickLink: ((URL) -> Void)? = nil,
checkoutDidEmitWebPixelEvent: ((ShopifyCheckoutSheetKit.PixelEvent) -> Void)? = nil
) {
self.checkoutDidComplete = checkoutDidComplete
self.checkoutDidFail = checkoutDidFail
self.checkoutDidCancel = checkoutDidCancel
self.shouldRecoverFromError = shouldRecoverFromError
self.checkoutDidClickLink = checkoutDidClickLink
self.checkoutDidEmitWebPixelEvent = checkoutDidEmitWebPixelEvent
}
}

extension View {
func walletButtonStyle(bg: Color = Color.black, cornerRadius: CGFloat? = nil) -> some View {
let defaultCornerRadius: CGFloat = 8
Expand Down
Loading
Loading