This is my first time working with PassKit and with SwiftUI on a big project. I'm trying to implement Apple Pay SwiftUI and since there isn't a native way to do so yet, I tried wrapped the PKPaymentAuthorizationViewController in UIViewControllerRepresentable, but I'm not sure if I'm doing it properly.
The view displays properly and seems to work when clicking on it to pay. I control showing the window by binding the view to an isPresentingApplePay bool (see below). The issues happen when the window should be dismissed. Tapping on the cancel
button does not dismiss the view; sometimes it doesn't even call the paymentAuthorizationViewControllerDidFinish delegate function. The same thing happens after submitting the payment. Sometimes the didFinish delegate gets called but the view is not dismissed. I tried passing the binding variable isPresentingApplePay and setting it to false from the didFinish but it doesn't do anything. The only way to get the view to disappear is to tap on any part outside of the apple pay window.
Does anyone know what I'm doing wrong? Is there anything I'm totally missing?
I get the apple pay window to show properly when pressing a button, by binding the view under an if statement
Here is my PKPaymentAuthorizationViewController wrapper:
import Foundation
import PassKit
import SwiftUI
struct ApplePayController: UIViewControllerRepresentable {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var userData: UserData
@Binding var purchase: Purchase
@Binding var isPresenting: Bool
let items: [PKPaymentSummaryItem]
func updateUIViewController(_ uiViewController: PKPaymentAuthorizationViewController, context: Context) {
}
typealias UIViewControllerType = PKPaymentAuthorizationViewController
func makeUIViewController(context: Context) -> PKPaymentAuthorizationViewController {
let applePayManager = ApplePayManager(items: items)
let apm = applePayManager.paymentViewController()!
apm.delegate = context.coordinator
return apm
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, PKPaymentAuthorizationViewControllerDelegate {
var parent: ApplePayController
init(_ parent: ApplePayController) {
self.parent = parent
}
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
controller.dismiss(animated: true) {
self.parent.isPresenting = false
}
}
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
print("did authorize payment")
}
func paymentAuthorizationViewControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationViewController) {
print("Will authorize payment")
}
}
class ApplePayManager: NSObject {
let currencyCode: String
let countryCode: String
let merchantID: String
let paymentNetworks: [PKPaymentNetwork]
let items: [PKPaymentSummaryItem]
init(items: [PKPaymentSummaryItem],
currencyCode: String = "USD",
countryCode: String = "US",
merchantID: String = "xxx.merchant.xxx",
paymentNetworks: [PKPaymentNetwork] = [PKPaymentNetwork.amex, PKPaymentNetwork.masterCard, PKPaymentNetwork.visa]) {
self.items = items
self.currencyCode = currencyCode
self.countryCode = countryCode
self.merchantID = merchantID
self.paymentNetworks = paymentNetworks
}
func paymentViewController() -> PKPaymentAuthorizationViewController? {
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: paymentNetworks) {
let request = PKPaymentRequest()
request.currencyCode = self.currencyCode
request.countryCode = self.countryCode
request.supportedNetworks = paymentNetworks
request.merchantIdentifier = self.merchantID
request.paymentSummaryItems = items
request.merchantCapabilities = [.capabilityCredit, .capabilityDebit]
return PKPaymentAuthorizationViewController(paymentRequest: request)
}
return nil
}
}
}
and this is how I show it in my UIView:
if isPresentingApplePay {
ApplePayController(purchase: self.$currentOrder.purchase, isPresenting: $isPresentingApplePay, items: self.createOrder(with: self.currentOrder.purchase)).environmentObject(self.userData)
}