From 2e7d1eaa8b94d2772b9b0fe8f86ca4ffc68a204a Mon Sep 17 00:00:00 2001 From: Mark Murray Date: Tue, 5 Mar 2024 14:32:33 +0000 Subject: [PATCH] Implement SFSafariViewController as fallback when checkoutDidFail --- .../CartViewController.swift | 26 ++++++------- .../CheckoutBridge.swift | 5 ++- .../CheckoutDelegate.swift | 4 +- .../CheckoutWebViewController.swift | 39 ++++++++++++++++--- .../Configuration.swift | 4 ++ 5 files changed, 56 insertions(+), 22 deletions(-) diff --git a/Samples/MobileBuyIntegration/MobileBuyIntegration/CartViewController.swift b/Samples/MobileBuyIntegration/MobileBuyIntegration/CartViewController.swift index dfd71f1e..d3d11bb5 100644 --- a/Samples/MobileBuyIntegration/MobileBuyIntegration/CartViewController.swift +++ b/Samples/MobileBuyIntegration/MobileBuyIntegration/CartViewController.swift @@ -162,9 +162,9 @@ extension CartViewController: CheckoutDelegate { dismiss(animated: true) } - func checkoutDidFail(errors: [ShopifyCheckoutSheetKit.CheckoutError]) { - print(#function, errors) - } +// func checkoutDidFail(errors: [ShopifyCheckoutSheetKit.CheckoutError]) { +// print(#function, errors) +// } func checkoutDidClickContactLink(url: URL) { if UIApplication.shared.canOpenURL(url) { @@ -173,16 +173,16 @@ extension CartViewController: CheckoutDelegate { } func checkoutDidFail(error: ShopifyCheckoutSheetKit.CheckoutError) { - switch error { - case .sdkError(let underlying): - print(#function, underlying) - forceCloseCheckout("Checkout Unavailable") - case .checkoutExpired(let message): forceCloseCheckout(message) - case .checkoutUnavailable(let message): forceCloseCheckout(message) - case .checkoutLiquidNotMigrated(let message): - print(#function, message) - forceCloseCheckout("Checkout Unavailable") - } +// switch error { +// case .sdkError(let underlying): +// print(#function, underlying) +// forceCloseCheckout("Checkout Unavailable") +// case .checkoutExpired(let message): forceCloseCheckout(message) +// case .checkoutUnavailable(let message): forceCloseCheckout(message) +// case .checkoutLiquidNotMigrated(let message): +// print(#function, message) +// forceCloseCheckout("Checkout Unavailable") +// } } func checkoutDidEmitWebPixelEvent(event: ShopifyCheckoutSheetKit.PixelEvent) { diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift index 58e9308b..9da1a220 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift @@ -95,10 +95,13 @@ extension CheckoutBridge { } init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) + throw NSError(domain: "com.shopify", code: 1, userInfo: [NSLocalizedDescriptionKey: "Fake Error"]) + + let container = try decoder.container(keyedBy: CodingKeys.self) let name = try container.decode(String.self, forKey: .name) + switch name { case "completed": let checkoutCompletedEventDecoder = CheckoutCompletedEventDecoder() diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutDelegate.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutDelegate.swift index a2556b3c..86e58cdc 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutDelegate.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutDelegate.swift @@ -52,9 +52,7 @@ extension CheckoutDelegate { handleUrl(url) } - public func checkoutDidFail(error: CheckoutError) throws { - throw error - } + public func checkoutDidFail(error: CheckoutError) throws {} private func handleUrl(_ url: URL) { if UIApplication.shared.canOpenURL(url) { diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift index c8559e48..d9e065d8 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift @@ -23,6 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO import UIKit import WebKit +import SafariServices class CheckoutWebViewController: UIViewController, UIAdaptivePresentationControllerDelegate { @@ -176,11 +177,6 @@ extension CheckoutWebViewController: CheckoutWebViewDelegate { delegate?.checkoutDidComplete(event: event) } - func checkoutViewDidFailWithError(error: CheckoutError) { - CheckoutWebView.invalidate() - delegate?.checkoutDidFail(error: error) - } - func checkoutViewDidClickLink(url: URL) { delegate?.checkoutDidClickLink(url: url) } @@ -193,4 +189,37 @@ extension CheckoutWebViewController: CheckoutWebViewDelegate { func checkoutViewDidEmitWebPixelEvent(event: PixelEvent) { delegate?.checkoutDidEmitWebPixelEvent(event: event) } + + func checkoutViewDidFailWithError(error: CheckoutError) { + CheckoutWebView.invalidate() + delegate?.checkoutDidFail(error: error) + + guard ShopifyCheckoutSheetKit.configuration.handleFallbackOnError else { + return + } + + switch error { + case .sdkError: fallthrough + case .checkoutUnavailable: fallthrough + case .checkoutLiquidNotMigrated: + self.fallbackToSafariViewController() + default: + break + } + } + + /// Private + + private func getRootViewController() -> UIViewController? { + return UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController + } + + private func fallbackToSafariViewController() { + dismiss(animated: true) { + let safariViewController = SFSafariViewController(url: self.checkoutURL) + if let rootViewController = self.getRootViewController() { + rootViewController.present(safariViewController, animated: true) + } + } + } } diff --git a/Sources/ShopifyCheckoutSheetKit/Configuration.swift b/Sources/ShopifyCheckoutSheetKit/Configuration.swift index ac10aeaa..971c8746 100644 --- a/Sources/ShopifyCheckoutSheetKit/Configuration.swift +++ b/Sources/ShopifyCheckoutSheetKit/Configuration.swift @@ -49,6 +49,10 @@ public struct Configuration { public var logger: Logger = NoOpLogger() public var title: String = NSLocalizedString("shopify_checkout_sheet_title", value: "Checkout", comment: "The title of the checkout sheet.") + + /// Trigger a SafariViewController in the event of a checkout error. + /// Set to "false" to disable this feature. + public var handleFallbackOnError = true } extension Configuration {