diff --git a/Sources/ShopifyCheckout/CheckoutView.swift b/Sources/ShopifyCheckout/CheckoutView.swift index e17a153f..ae357a90 100644 --- a/Sources/ShopifyCheckout/CheckoutView.swift +++ b/Sources/ShopifyCheckout/CheckoutView.swift @@ -122,7 +122,7 @@ extension CheckoutView: WKNavigationDelegate { } if isExternalLink(action) || isMailOrTelLink(url) { - viewDelegate?.checkoutViewDidClickLink(url: url) + viewDelegate?.checkoutViewDidClickLink(url: removeExternalParam(url)) decisionHandler(.cancel) return } @@ -170,9 +170,24 @@ extension CheckoutView: WKNavigationDelegate { } private func isExternalLink(_ action: WKNavigationAction) -> Bool { - return action.navigationType == .linkActivated && action.targetFrame == nil + if action.navigationType == .linkActivated && action.targetFrame == nil { return true } + + guard let url = action.request.url else { return false } + guard let url = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false } + + guard let openExternally = url.queryItems?.first(where: { $0.name == "open_externally" })?.value else { return false } + + return openExternally.lowercased() == "true" || openExternally == "1" } + private func removeExternalParam(_ url: URL) -> URL { + guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + return url + } + urlComponents.queryItems = urlComponents.queryItems?.filter { !($0.name == "open_externally") } + return urlComponents.url ?? url + } + private func isMailOrTelLink(_ url: URL) -> Bool { return ["mailto", "tel"].contains(url.scheme) } diff --git a/Tests/ShopifyCheckoutTests/CheckoutViewTests.swift b/Tests/ShopifyCheckoutTests/CheckoutViewTests.swift index 5e60ec80..f22fd562 100644 --- a/Tests/ShopifyCheckoutTests/CheckoutViewTests.swift +++ b/Tests/ShopifyCheckoutTests/CheckoutViewTests.swift @@ -86,6 +86,23 @@ class CheckoutViewTests: XCTestCase { wait(for: [didClickLinkExpectation], timeout: 1) } + func testURLLinkDelegationWithExternalParam() { + let link = URL(string: "https://www.shopify.com/legal/privacy/app-users?open_externally=true")! + + let delegate = MockCheckoutViewDelegate() + let didClickLinkExpectation = expectation( + description: "checkoutViewDidClickLink was called" + ) + delegate.didClickLinkExpectation = didClickLinkExpectation + view.viewDelegate = delegate + + view.webView(view, decidePolicyFor: MockExternalNavigationAction(url: link, navigationType: .other)) { policy in + XCTAssertEqual(policy, .cancel) + } + + wait(for: [didClickLinkExpectation], timeout: 1) + } + func test410responseOnCheckoutURLCodeDelegation() { view.load(checkout: URL(string: "http://shopify1.shopify.com/checkouts/cn/123")!) let link = view.url! diff --git a/Tests/ShopifyCheckoutTests/Mocks/MockNavigationAction.swift b/Tests/ShopifyCheckoutTests/Mocks/MockNavigationAction.swift index ea0ad805..57ee7caa 100644 --- a/Tests/ShopifyCheckoutTests/Mocks/MockNavigationAction.swift +++ b/Tests/ShopifyCheckoutTests/Mocks/MockNavigationAction.swift @@ -38,21 +38,23 @@ class MockNavigationAction: WKNavigationAction { class MockExternalNavigationAction: WKNavigationAction { private let mockRequest: URLRequest + private let navType: WKNavigationType override var request: URLRequest { return mockRequest } override var navigationType: WKNavigationType { - return .linkActivated + return self.navType } override var targetFrame: WKFrameInfo? { return nil } - init(url: URL) { + init(url: URL, navigationType: WKNavigationType = .linkActivated) { self.mockRequest = URLRequest(url: url) + self.navType = navigationType super.init() } }