Introduction

In the realm of modern iOS development, where responsiveness, scalability, and maintainability reign supreme, the power of reactive programming shines brightly. At the heart of this paradigm lies RxSwift, a powerful framework that empowers developers to tackle complex asynchronous operations with elegance and precision.

In this article, we delve into the transformative capabilities of RxSwift, particularly in the context of rewriting delegate methods. Delegates have long been a cornerstone of iOS development, facilitating communication between objects. However, as applications grow in complexity, managing delegate callbacks can become cumbersome and error-prone.

Enter the proxy pattern, a design solution that leverages the principles of RxSwift to streamline delegate management. By reimagining traditional delegate methods through a reactive lens, developers can unlock new levels of flexibility and efficiency in their codebase.

One such challenge lies in managing Apple Pay payment delegates, which traditionally involve multiple callback methods for handling payment authorization, completion, and errors. While delegates have served as a reliable communication mechanism, their inherent complexities can often lead to spaghetti-like code structures and debugging headaches.

Apple Pay under the hood

Join us on a journey through the intricacies of RxSwift as we demonstrate the power of rewriting Apple Pay payment delegates using the proxy pattern, offering a glimpse into the future of iOS development practices.

Understanding Traditional Delegate Methods in Apple Pay

Apple Pay delegates are used to handle various aspects of the payment process, including authorization, completion, error handling, and merchant session requests.

Delegates involved in Apple Pay, such as PKPaymentAuthorizationViewControllerDelegate, typically conform to specific protocols defined by Apple. These protocols outline the methods that the delegate must implement to handle payment-related events.

Apple Pay delegate protocols contain both mandatory and optional methods. Mandatory methods must be implemented to ensure proper functioning of the payment process, while optional methods provide flexibility for customizing behavior or handling additional events.

The PKPaymentAuthorizationViewController (or other relevant classes) invokes delegate methods when specific events occur during the payment process, such as when payment authorization is completed or when errors occur.

extension CheckoutStepsViewController: PKPaymentAuthorizationViewControllerDelegate {
    func paymentAuthorizationViewController(
        _: PKPaymentAuthorizationViewController,
        didAuthorizePayment payment: PKPayment,
        completion: @escaping (PKPaymentAuthorizationStatus) -> Void
    ) {
        /// Authorize the payment
        applePaymentRelay.accept(
            .confirm(
                (payment,
                 completion
                )
            )
        )
    }

    func paymentAuthorizationViewControllerDidFinish(_: PKPaymentAuthorizationViewController) {
        dismiss(animated: true) { [weak self] in
            /// finalize the payment and confirm the order (handle also cancel state) after dismiss the PKPaymentAuthorizationViewController
            self?.finalizeApplePayPayment()
        }
    }
}


You think about the rule of the appPaymentRelay attribut!. We use it it to handle the state of our order.

private var applePaymentRelay = PublishRelay<ApplePayAuthorizationStatus>()
private enum ApplePayAuthorizationStatus {
    case confirm((
        PKPayment,
        (PKPaymentAuthorizationStatus) -> Void)
    )
    
    case cancel
}

The PKPayment is a special type provide by Apple to encapsulate all the user payment data like billing address and other information like the token. The token will be used further to valide the order using the back end API.

open class PKPayment : NSObject {
    
    // A PKPaymentToken which contains an encrypted payment credential.
    open var token: PKPaymentToken { get }

  // the requiredBillingAddressFields property of the PKPaymentRequest.
    @available(iOS 9.0, *)
    open var billingContact: PKContact? { get }
....
}

Even you get the token, you should check the call back @escaping (PKPaymentAuthorizationStatus) -> Void which return a PKPaymentAuthorizationStatus. This one is en enum that contains different status of the transaction between Apple Pay service and the banking system.

public enum PKPaymentAuthorizationStatus : Int, @unchecked Sendable {

    case success = 0 // Merchant auth'd (or expects to auth) the transaction successfully.

    case failure = 1 // Merchant failed to auth the transaction
    case invalidBillingPostalAddress = 2 // Supplied billing address is insufficient or otherwise invalid

...
}

How to setup the PKPaymentAuthorizationViewController

Certainly! The PKPaymentAuthorizationViewController is a key component in the Apple Pay framework for iOS. Its primary role is to provide a user interface for initiating and managing the payment authorization process within an iOS app. The PKPaymentAuthorizationViewController presents a standard payment interface to the user, which includes details about the payment request, such as the total amount due, the merchant name, and the items being purchased.

Setting up the PKPaymentAuthorizationViewController in Swift involves several steps, including configuring the payment request, presenting the authorization view controller, and implementing the necessary delegate methods to handle payment authorization. Here’s a basic outline of how to set up the PKPaymentAuthorizationViewController:

  • Import the necessary frameworks:
import PassKit

  • Configure the payment request:
// Create a payment request
let paymentRequest = PKPaymentRequest()

let supportedNetworks: [PKPaymentNetwork] {
        return [
            .visa,
            .masterCard,
            .amex,
        ]
    }

// Configure the payment request with merchant information

paymentRequest.merchantIdentifier = "your_merchant_identifier"
paymentRequest.countryCode = "US"
paymentRequest.currencyCode = "USD"
paymentRequest.merchantCapabilities = PKMerchantCapability.capability3DS
paymentRequest.supportedNetworks = supportedNetworks

// Set the payment summary items (total amount due)
let totalAmount = PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(string: "10.00"))
paymentRequest.paymentSummaryItems = [totalAmount]

// Optional: Set shipping methods and shipping address
// paymentRequest.shippingMethods = ...
// paymentRequest.requiredShippingAddressFields = ...
  • Create and present the PKPaymentAuthorizationViewController:
// Check if Apple Pay is available
if PKPaymentAuthorizationViewController.canMakePayments() {
    // Create the payment authorization view controller
    let paymentAuthorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)
    
    // Set the delegate (implement delegate methods to handle payment authorization)
    paymentAuthorizationViewController.delegate = self
    
    // Present the payment authorization view controller
    if let viewController = paymentAuthorizationViewController {
        present(viewController, animated: true, completion: nil)
    }
} else {
    // Apple Pay is not available, handle accordingly
    print("Apple Pay is not available on this device.")
}

DataBinding using RxSwift

As we mentioned we use applePaymentRelay in order to trigger events after delegates methods was invoked.

applePaymentRelay
     .bind { [weak self] authorizationStatus in
         switch authorizationStatus {
            case .confirm(let paymentResult):
            /// paymentResult is a tuple that contains both the PKPayment and the callBack (PKPaymentAuthorizationStatus) -> Void
/// We call the backend to confirm the order
            self?.confirmApplePayPayment(
                 paymentResult.0,
                 completion: paymentResult.1
             )
                
            case .cancel:
             /// call back end to cancel the order
               self?.cancelApplePayPayment()
          }
     }
     .disposed(by: disposeBag)

Absolutely, it’s a good practice to separate concerns and keep your view controllers lean. We can optimize the code by moving the logic related to setting up the payment request and handling payment authorization into separate classes. This follows the principles of encapsulation and separation of concerns. But what if we can transform all this delegates flow to be a reactive flow using the proxy pattern. This will be the subject of the second part of our article.

Bibliography

https://codeburst.io/how-does-apple-pay-actually-work-f52f7d9348b7

https://developer.apple.com/documentation/passkit_apple_pay_and_wallet/pkpaymentauthorizationviewcontroller

Leave A Comment

Solve : *
13 − 8 =