Skip to content

Commit

Permalink
Expose "checkoutDidClickLink" handler, implement sensible defaults (#6)
Browse files Browse the repository at this point in the history
* BREAKING CHANGE: only expose one clicked link function on delegate
* Create default action for delegate functions, starting with checkoutDidClickLink

---------

Co-authored-by: Mark Murray <[email protected]>
  • Loading branch information
cianbuckley and markmur authored Oct 2, 2023
1 parent 137786a commit f4982c4
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 34 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ extension MyViewController: ShopifyCheckoutDelegate {
// The buyer encountered an error during checkout.
}

func checkoutDidClickContactLink(url: URL) {
// Called when the buyer clicked a link which points to an email address or telephone number via `mailto:` or `tel:`.
func checkoutDidClickLink(url: URL) {
// Called when the buyer clicked a link e.g mail address or telephone number via `mailto:` or `tel:` or `http` links directed outside the application.
}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,6 @@ extension CartViewController: CheckoutDelegate {
dismiss(animated: true)
}

func checkoutDidClickContactLink(url: URL) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}

func checkoutDidFail(errors: [ShopifyCheckout.CheckoutError]) {
print(#function, errors)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ class ProductViewController: UIViewController, CheckoutDelegate {
dismiss(animated: true)
}

func checkoutDidClickContactLink(url: URL) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}

func checkoutDidFail(errors: [CheckoutError]) {
print(errors)
}
Expand Down
21 changes: 17 additions & 4 deletions Sources/ShopifyCheckout/CheckoutDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO
*/

import Foundation
import UIKit

/// A delegate protocol for managing checkout lifecycle events.
public protocol CheckoutDelegate: AnyObject {
Expand All @@ -31,10 +32,22 @@ public protocol CheckoutDelegate: AnyObject {
/// Tells the delegate that the checkout was cancelled by the buyer.
func checkoutDidCancel()

/// Tells the delegate that the buyer clicked a link which points to an
/// email address or telephone number via `mailto:` or `tel:`.
func checkoutDidClickContactLink(url: URL)

/// Tells the delegate that the checkout encoutered one or more errors.
func checkoutDidFail(errors: [CheckoutError])

/// Tells te delegate that the buyer clicked a link
/// This includes email address or telephone number via `mailto:` or `tel:` or `http` links directed outside the application.
func checkoutDidClickLink(url: URL)
}

extension CheckoutDelegate {
public func checkoutDidClickLink(url: URL) {
handleUrl(url)
}

private func handleUrl(_ url: URL) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
}
21 changes: 17 additions & 4 deletions Sources/ShopifyCheckout/CheckoutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protocol CheckoutViewDelegate: AnyObject {

func checkoutViewDidFinishNavigation()

func checkoutViewDidClickContactLink(url: URL)
func checkoutViewDidClickLink(url: URL)

func checkoutViewDidFailWithError(_ error: Error)
}
Expand Down Expand Up @@ -113,14 +113,19 @@ extension CheckoutView: WKScriptMessageHandler {

extension CheckoutView: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor action: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = action.request.url, ["mailto", "tel"].contains(url.scheme) else {

guard let url = action.request.url else {
decisionHandler(.allow)
return
}

delegate?.checkoutViewDidClickContactLink(url: url)
if isExternalLink(action) || isMailOrTelLink(url) {
delegate?.checkoutViewDidClickLink(url: url)
decisionHandler(.cancel)
return
}

decisionHandler(.cancel)
decisionHandler(.allow)
}

func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
Expand All @@ -130,6 +135,14 @@ extension CheckoutView: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
delegate?.checkoutViewDidFinishNavigation()
}

private func isExternalLink(_ action: WKNavigationAction) -> Bool {
return action.navigationType == .linkActivated && action.targetFrame == nil
}

private func isMailOrTelLink(_ url: URL) -> Bool {
return ["mailto", "tel"].contains(url.scheme)
}
}

extension CheckoutView {
Expand Down
8 changes: 4 additions & 4 deletions Sources/ShopifyCheckout/CheckoutViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ extension CheckoutViewController: CheckoutViewDelegate {
delegate?.checkoutDidComplete()
}

func checkoutViewDidClickContactLink(url: URL) {
delegate?.checkoutDidClickContactLink(url: url)
}

func checkoutViewDidFailWithError(_ error: Error) {
delegate?.checkoutDidFail(errors: [.internalError(underlying: error)])
}

func checkoutViewDidClickLink(url: URL) {
delegate?.checkoutDidClickLink(url: url)
}
}
33 changes: 25 additions & 8 deletions Tests/ShopifyCheckoutTests/CheckoutViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,50 @@ class CheckoutViewTests: XCTestCase {
let link = URL(string: "mailto:[email protected]")!

let delegate = MockCheckoutViewDelegate()
let didClickContactLinkExpectation = expectation(
description: "checkoutViewDidClickContactLink was called"
let didClickLinkExpectation = expectation(
description: "checkoutViewDidClickLink was called"
)
delegate.didClickContactLinkExpectation = didClickContactLinkExpectation
delegate.didClickLinkExpectation = didClickLinkExpectation
view.delegate = delegate

view.webView(view, decidePolicyFor: MockNavigationAction(url: link)) { policy in
XCTAssertEqual(policy, .cancel)
}

wait(for: [didClickContactLinkExpectation], timeout: 1)
wait(for: [didClickLinkExpectation], timeout: 1)
}

func testPhoneContactLinkDelegation() {
let link = URL(string: "tel:1234567890")!

let delegate = MockCheckoutViewDelegate()
let didClickContactLinkExpectation = expectation(
description: "checkoutViewDidClickContactLink was called"
let didCLickLinkExpectation = expectation(
description: "checkoutViewDidClickLink was called"
)
delegate.didClickContactLinkExpectation = didClickContactLinkExpectation
delegate.didClickLinkExpectation = didCLickLinkExpectation
view.delegate = delegate

view.webView(view, decidePolicyFor: MockNavigationAction(url: link)) { policy in
XCTAssertEqual(policy, .cancel)
}

wait(for: [didClickContactLinkExpectation], timeout: 1)
wait(for: [didCLickLinkExpectation], timeout: 1)
}

func testURLLinkDelegation() {
let link = URL(string: "https://www.shopify.com/legal/privacy/app-users")!

let delegate = MockCheckoutViewDelegate()
let didClickLinkExpectation = expectation(
description: "checkoutViewDidClickLink was called"
)
delegate.didClickLinkExpectation = didClickLinkExpectation
view.delegate = delegate

view.webView(view, decidePolicyFor: MockExternalNavigationAction(url: link)) { policy in
XCTAssertEqual(policy, .cancel)
}

wait(for: [didClickLinkExpectation], timeout: 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class MockCheckoutViewDelegate: CheckoutViewDelegate {

var didClickContactLinkExpectation: XCTestExpectation?

var didClickLinkExpectation: XCTestExpectation?

var didFailWithErrorExpectation: XCTestExpectation?

func checkoutViewDidStartNavigation() {
Expand All @@ -51,6 +53,10 @@ class MockCheckoutViewDelegate: CheckoutViewDelegate {
didClickContactLinkExpectation?.fulfill()
}

func checkoutViewDidClickLink(url: URL) {
didClickLinkExpectation?.fulfill()
}

func checkoutViewDidFailWithError(_ error: Error) {
didFailWithErrorExpectation?.fulfill()
}
Expand Down
21 changes: 21 additions & 0 deletions Tests/ShopifyCheckoutTests/Mocks/MockNavigationAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,24 @@ class MockNavigationAction: WKNavigationAction {
super.init()
}
}

class MockExternalNavigationAction: WKNavigationAction {
private let mockRequest: URLRequest

override var request: URLRequest {
return mockRequest
}

override var navigationType: WKNavigationType {
return .linkActivated
}

override var targetFrame: WKFrameInfo? {
return nil
}

init(url: URL) {
self.mockRequest = URLRequest(url: url)
super.init()
}
}

0 comments on commit f4982c4

Please sign in to comment.