Esta é minha primeira vez trabalhando com PassKit e com SwiftUI em um grande projeto. Estou tentando implementar o Apple Pay SwiftUI e, como ainda não existe uma maneira nativa de fazer isso, tentei empacotar PKPaymentAuthorizationViewController em UIViewControllerRepresentable, mas não tenho certeza se estou fazendo isso corretamente.
A visualização é exibida corretamente e parece funcionar ao clicar nela para pagar. Eu controlo a exibição da janela vinculando a visualização a um bool isPresentingApplePay (veja abaixo). Os problemas acontecem quando a janela deve ser descartada. Tocar no cancelbotão não descarta a visualização; às vezes, nem mesmo chama a função de delegado paymentAuthorizationViewControllerDidFinish. O mesmo acontece após o envio do pagamento. Às vezes, o delegado didFinish é chamado, mas a visualização não é descartada. Tentei passar a variável de ligação isPresentingApplePay e defini-la como false no didFinish, mas não faz nada. A única maneira de fazer a vista desaparecer é tocar em qualquer parte fora da janela de pagamento da apple.
Alguém sabe o que estou fazendo de errado? Há algo que estou totalmente perdendo?
Eu faço com que a janela de pagamento da maçã seja exibida corretamente ao pressionar um botão, vinculando a visualização a um if statement
Aqui está meu wrapper PKPaymentAuthorizationViewController:
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
}
}
}
e é assim que eu mostro em meu UIView:
if isPresentingApplePay {
ApplePayController(purchase: self.$currentOrder.purchase, isPresenting: $isPresentingApplePay, items: self.createOrder(with: self.currentOrder.purchase)).environmentObject(self.userData)
}
Eu enfrentei um problema semelhante e resolvi usando outra abordagem que não faz interface com UIViewControllerRepresentable. Em vez disso, criei uma classe separada para lidar com o Apple Pay como seguir
import PassKit
typealias PaymentCompletionHandler = (Bool) -> Void
class PaymentHandler: NSObject {
static let supportedNetworks: [PKPaymentNetwork] = [
.amex,
.masterCard,
.visa
]
var paymentController: PKPaymentAuthorizationController?
var paymentSummaryItems = [PKPaymentSummaryItem]()
var paymentStatus = PKPaymentAuthorizationStatus.failure
var completionHandler: PaymentCompletionHandler?
func startPayment(completion: @escaping PaymentCompletionHandler) {
let amount = PKPaymentSummaryItem(label: "Ammount", amount: NSDecimalNumber(string: "8.88"), type: .final)
let tax = PKPaymentSummaryItem(label: "Tax", amount: NSDecimalNumber(string: "1.12"), type: .final)
let total = PKPaymentSummaryItem(label: "ToTal", amount: NSDecimalNumber(string: "10.00"), type: .pending)
paymentSummaryItems = [amount, tax, total];
completionHandler = completion
// Create our payment request
let paymentRequest = PKPaymentRequest()
paymentRequest.paymentSummaryItems = paymentSummaryItems
paymentRequest.merchantIdentifier = "merchant.com.YOURDOMAIN.YOURAPPNAME"
paymentRequest.merchantCapabilities = .capability3DS
paymentRequest.countryCode = "US"
paymentRequest.currencyCode = "USD"
paymentRequest.requiredShippingContactFields = [.phoneNumber, .emailAddress]
paymentRequest.supportedNetworks = PaymentHandler.supportedNetworks
// Display our payment request
paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentController?.delegate = self
paymentController?.present(completion: { (presented: Bool) in
if presented {
NSLog("Presented payment controller")
} else {
NSLog("Failed to present payment controller")
self.completionHandler!(false)
}
})
}
}
/*
PKPaymentAuthorizationControllerDelegate conformance.
*/
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
// Perform some very basic validation on the provided contact information
if payment.shippingContact?.emailAddress == nil || payment.shippingContact?.phoneNumber == nil {
paymentStatus = .failure
} else {
// Here you would send the payment token to your server or payment provider to process
// Once processed, return an appropriate status in the completion handler (success, failure, etc)
paymentStatus = .success
}
completion(paymentStatus)
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss {
DispatchQueue.main.async {
if self.paymentStatus == .success {
self.completionHandler!(true)
} else {
self.completionHandler!(false)
}
}
}
}
}
E usei o seguinte código de botão para pop-up do Apple Pay.
struct ContentView: View {
let paymentHandler = PaymentHandler()
var body: some View {
Button(action: {
self.paymentHandler.startPayment { (success) in
if success {
print("Success")
} else {
print("Failed")
}
}
}, label: {
Text("PAY WITH APPLE")
.font(Font.custom("HelveticaNeue-Bold", size: 16))
.padding(10)
.foregroundColor(.white)
}
}
}
Finalmente, funciona bem
Uma versão que funciona para apresentar o PKPaymentAuthorizationViewControllerpode ser encontrada aqui: Como apresentar PKPaymentAuthorizationViewController corretamente em um projeto somente SwiftUI?
O wrapper que estou usando é o seguinte:
import PassKit
import SwiftUI
struct ApplePayWrapper: UIViewControllerRepresentable {
typealias UIViewControllerType = PKPaymentAuthorizationViewController
let request: PKPaymentRequest
func makeUIViewController(context: Context) -> PKPaymentAuthorizationViewController {
let applePayController = PKPaymentAuthorizationViewController(paymentRequest: request)
return applePayController!
}
func updateUIViewController(_ uiViewController: PKPaymentAuthorizationViewController, context: Context) {
// Nothing
}
}
Estou usando um ViewModelpara atuar como um delegate, você precisa adicioná-lo aos campos de structdepois let request: PKPaymentRequeste definir o delegado ao criar oPKPaymentAuthorizationViewController
Você nem precisa usar UIViewControllerRepresentableou usar o UIKitSwiftUI. Use PKPaymentAuthorizationController, NÃO a versão do controlador de visualização :
let request = PKPaymentRequest()
request.paymentSummaryItems = ...
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present { [weak self] presented in
// Apple Pay presented from scene window
}
https://developer.apple.com/forums/thread/649662?answerId=641228022#641228022
A resposta aceita por Taif é muito boa PaymentHandlerlateralmente.
Mas, para usar o botão real do Apple Pay, parece que você estava tendo exatamente o mesmo problema que eu! Basicamente, você deve colocar seu UIViewRepresentableem um ButtonStylee, em seguida, estilizar um botão SwiftUI com ele. Não sei por que isso é verdade, mas aqui está o código como ele deve funcionar:
import SwiftUI
import UIKit
import PassKit
struct PaymentButton: View {
let paymentHandler = PaymentHandler() //As defined by Taif
var body: some View {
Button(action: {
// Using the code from Tarif!
self.paymentHandler.startPayment { success in
if success {
print("Success")
} else {
print("Failed")
}
}
}, label: { EmptyView() } )
.buttonStyle(PaymentButtonStyle())
}
}
struct PaymentButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
return PaymentButtonHelper()
}
}
struct PaymentButtonHelper: View {
var body: some View {
PaymentButtonRepresentable()
.frame(minWidth: 100, maxWidth: 400)
.frame(height: 60)
.frame(maxWidth: .infinity)
}
}
extension PaymentButtonHelper {
struct PaymentButtonRepresentable: UIViewRepresentable {
var button: PKPaymentButton {
let button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .black) /*customize here*/
button.cornerRadius = 4.0 /* also customize here */
return button
}
func makeUIView(context: Context) -> PKPaymentButton {
return button
}
func updateUIView(_ uiView: PKPaymentButton, context: Context) { }
}
Então, para usá-lo, você diria
struct ContentView: View {
var body: some View {
PaymentButton()
}
}
Jana Duggar foi aberta sobre sua busca pelo amor. Aqui está tudo o que ela disse sobre o assunto e sua janela de cinco anos para o casamento.
O astro de 'Outlander', Sam Heughan, revelou recentemente o que vai levar do set para relembrar Jamie Fraser, o papel que o tornou uma estrela.
'She Loves You' foi escrita em uma hora, gravada em um dia, e foi a música dos Beatles com uma de suas melhores apresentações de sua carreira.
Dolly Parton e sua avó Bessie tiveram um relacionamento especial. Vovó Parton estava muito doente, mas isso não impediu Dolly de pregar uma peça nela.
Você pode achar que o carvalho ou a nogueira são madeiras resistentes, mas quando se trata da madeira mais dura do mundo, elas nem chegam perto.
O oceano é repleto de beleza, mas também esconde algumas das criaturas marinhas mais assustadoras do planeta. Muitos desses animais espreitam nas profundezas do oceano, no mundo escuro e de alta pressão do fundo do mar.
Se você está enfrentando criaturas hostis ou se preparando para cenários PvP, conhecer os melhores encantamentos de espada no Minecraft pode te dar uma grande vantagem. Encantar espadas permite causar mais dano, aumentar a quantidade de itens obtidos de criaturas e prolongar a durabilidade da sua espada.
Quando as pessoas falam sobre países socialistas, geralmente imaginam o controle total do governo e a ausência de propriedade privada. Mas, na prática, as economias socialistas variam muito.
“Children” traz uma participação especial de grande nome e algumas notícias devastadoras
Este RAV4 está em excelentes condições e está preparado para um jogo de cadeiras musicais.
Imagens de drone capturaram bombeiros parecendo lutar para apagar o incêndio.
Eyes of Wakanda está diretamente relacionado ao MCU, além de atualizações sobre X-Men '97, What If..., Demolidor e muito mais.
O anel de noivado de Kate Middleton pertenceu à Princesa Diana antes do Príncipe William pedi-la em casamento. Descubra tudo o que há para saber sobre a peça histórica aqui.
John Cleese se casou com sua esposa, Jennifer Wade, em 2012. Aqui está tudo o que você precisa saber sobre a esposa de John Cleese, Jennifer Wade.
Patton Oswalt se casou com sua esposa, a também atriz Meredith Salenger, em 2017. Aqui está tudo o que você precisa saber sobre a esposa de Patton Oswalt, Meredith Salenger.
Mena Suvari é casada com o marido Michael Hope desde 2018. Aqui está tudo o que você precisa saber sobre o marido de Mena Suvari.
Isso me atinge De repente, como tantas vezes acontece É o meio do caminho tudo de novo <Não, não aquele no Pacífico, seu marrom, aquele no carnaval> Todas as manchetes em voz alta…..
Em 2022, com o lançamento do GPT-3, a habilidade de escrita parece se tornar menos importante. Com a IA, pessoas com redação ruim também podem gerar funcionários de qualidade.
No mundo acelerado e competitivo de hoje, a carreira desempenha um papel significativo no crescimento pessoal, na estabilidade financeira e na satisfação geral com a vida. No entanto, muitos indivíduos encontram-se presos em um ciclo implacável de estagnação e insatisfação, definhando gradualmente em suas vidas profissionais.
Na semana passada, notei um comunicado de imprensa, enviado via PressGazette (um site de notícias da mídia britânica). O artigo anunciava que a Acast, a empresa de publicidade e hospedagem de podcast Scandi, lideraria um consórcio de "provavelmente os editores mais influentes" em podcasting.