Add window delegate, allow secondary windows outside of app extensions

This commit is contained in:
David Estes 2021-02-17 12:27:54 -08:00
parent 9ceba60808
commit b3cb8afbdb
3 changed files with 57 additions and 28 deletions

View File

@ -27,6 +27,7 @@ extension PKPaymentAuthorizationController {
class func stp_controller(
with paymentRequest: PKPaymentRequest,
apiClient: STPAPIClient,
presentationWindow: UIWindow?,
onShippingAddressSelection: @escaping STPShippingAddressSelectionBlock,
onShippingMethodSelection: @escaping STPShippingMethodSelectionBlock,
onPaymentAuthorization: @escaping STPPaymentAuthorizationBlock,
@ -40,6 +41,7 @@ extension PKPaymentAuthorizationController {
delegate.onPaymentAuthorization = onPaymentAuthorization
delegate.onPaymentMethodCreation = onPaymentMethodCreation
delegate.onFinish = onFinish
delegate.presentationWindow = presentationWindow
let controller = self.init(paymentRequest: paymentRequest)
controller.delegate = delegate
objc_setAssociatedObject(
@ -65,6 +67,7 @@ class STPBlockBasedApplePayDelegate: NSObject, PKPaymentAuthorizationControllerD
var onPaymentMethodCreation: STPApplePayPaymentMethodHandlerBlock?
var onFinish: STPPaymentCompletionBlock?
var lastError: Error?
var presentationWindow: UIWindow?
var didSucceed = false
// Remove all this once we drop iOS 11 support
@ -156,6 +159,10 @@ class STPBlockBasedApplePayDelegate: NSObject, PKPaymentAuthorizationControllerD
onFinish?(.userCancellation, nil)
}
}
func presentationWindow(for controller: PKPaymentAuthorizationController) -> UIWindow? {
presentationWindow
}
}
typealias STPPaymentAuthorizationStatusCallback = (PKPaymentAuthorizationStatus) -> Void

View File

@ -98,7 +98,8 @@ import PassKit
super.init()
authorizationController?.delegate = self
}
private var presentationWindow: UIWindow?
/// Presents the Apple Pay sheet, starting the payment process.
/// @note This method should only be called once; create a new instance of STPApplePayContext every time you present Apple Pay.
/// @deprecated A presenting UIViewController is no longer needed. Use presentApplePay(completion:) instead.
@ -109,7 +110,8 @@ import PassKit
@available(*, deprecated, message: "Use `presentApplePay(completion:)` instead.", renamed: "presentApplePay(completion:)")
public func presentApplePay(on viewController: UIViewController, completion: STPVoidBlock? = nil)
{
self.presentApplePay(completion: completion)
let window = viewController.viewIfLoaded?.window
self.presentApplePay(from: window, completion: completion)
}
/// Presents the Apple Pay sheet, starting the payment process.
@ -117,35 +119,50 @@ import PassKit
/// - Parameters:
/// - completion: Called after the Apple Pay sheet is presented
@objc(presentApplePayWithCompletion:)
@available(iOSApplicationExtension, unavailable, message: "Use `presentApplePay(from:completion:)` in App Extensions.")
@available(macCatalystApplicationExtension, unavailable, message: "Use `presentApplePay(from:completion:)` in App Extensions.")
public func presentApplePay(completion: STPVoidBlock? = nil)
{
if didPresentApplePay {
assert(
false,
"This method should only be called once; create a new instance every time you present Apple Pay."
)
return
}
didPresentApplePay = true
guard let applePayController = self.authorizationController else {
assert(
false,
"This method should only be called once; create a new instance of STPApplePayContext every time you present Apple Pay."
)
return
}
// This instance must live so that the apple pay sheet is dismissed; until then, the app is effectively frozen.
objc_setAssociatedObject(
applePayController, UnsafeRawPointer(&kSTPApplePayContextAssociatedObjectKey), self,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
applePayController.present { (presented) in
stpDispatchToMainThreadIfNecessary {
completion?()
}
}
let window = UIApplication.shared.windows.first { $0.isKeyWindow }
self.presentApplePay(from: window, completion: completion)
}
/// Presents the Apple Pay sheet, starting the payment process.
/// @note This method should only be called once; create a new instance of STPApplePayContext every time you present Apple Pay.
/// - Parameters:
/// - window: The UIWindow to host the Apple Pay sheet
/// - completion: Called after the Apple Pay sheet is presented
@objc(presentApplePayFromWindow:withCompletion:)
public func presentApplePay(from window: UIWindow?, completion: STPVoidBlock? = nil)
{
presentationWindow = window
if didPresentApplePay {
assert(
false,
"This method should only be called once; create a new instance every time you present Apple Pay."
)
return
}
didPresentApplePay = true
guard let applePayController = self.authorizationController else {
assert(
false,
"This method should only be called once; create a new instance of STPApplePayContext every time you present Apple Pay."
)
return
}
// This instance must live so that the apple pay sheet is dismissed; until then, the app is effectively frozen.
objc_setAssociatedObject(
applePayController, UnsafeRawPointer(&kSTPApplePayContextAssociatedObjectKey), self,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
applePayController.present { (presented) in
stpDispatchToMainThreadIfNecessary {
completion?()
}
}
}
/// The STPAPIClient instance to use to make API requests to Stripe.
/// Defaults to `STPAPIClient.shared`.
@ -313,6 +330,10 @@ import PassKit
}
}
}
public func presentationWindow(for controller: PKPaymentAuthorizationController) -> UIWindow? {
return presentationWindow
}
// MARK: - Helpers
func _completePayment(

View File

@ -632,6 +632,7 @@ public class STPPaymentContext: NSObject, STPAuthenticationContext,
strongSelf.applePayC = PKPaymentAuthorizationController.stp_controller(
with: paymentRequest,
apiClient: strongSelf.apiClient,
presentationWindow: strongSelf.hostViewController?.viewIfLoaded?.window,
onShippingAddressSelection: shippingAddressHandler,
onShippingMethodSelection: shippingMethodHandler,
onPaymentAuthorization: paymentHandler,