WebView 안의 링크를 Safari에서 열기

Posted on 2022-02-21 by GKSRUDTN99
Swift&Xcode Swift WebView

# 문제

현재 개발중인 앱에서 상품의 상세 정보를 보여줄 때,

일부 정보를 html String 형태로 받아, 이를 클라이언트의 WebView에 로드시켜 보여주고 있었습니다.

import Foundation
import UIKit
import WebKit

class ViewController: {
    @IBOutlet weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        webView.navigationDelegate = self
        webView.loadHTMLString(html, baseURL: Bundle.main.bundleURL)
    }
}

그런데 최근, 위 방식으로 보여지는 html에 포함된 링크를 눌렀을 때,

Safari로 이동하여 해당 링크를 열 수 있도록 기능 추가를 요청받았습니다.


# 해결

WkWebView에 관련된 문서와 구글링을 통해 알아보니

WebView가 'GET'요청 또는 'POST'요청을 보낼 때,

WKNavigationDelegate 내의

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)

함수가 호출된다고 합니다.


이를 이용해, http 또는 https 프로토콜을 사용하는 요청을 확인했을 때, Safari를 통해 해당 링크를 열고,

WebView에서 진행하는 navigation은 cancel되도록 구현하였습니다.

extension ViewController: WKNavigationDelegate {
  func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if 
      let url = navigationAction.request.url,
      let scheme = url.scheme,
      scheme == "https" || scheme == "http",
      UIApplication.shared.canOpenURL(url) 
    {
      UIApplication.shared.open(url, options: [:], completionHandler: nil)
      decisionHandler(.cancel)
    } else {
      decisionHandler(.allow)
    }
  }
}


# 추가 - 22.02.22

http와 https 프로토콜을 사용하는 요청이 웹뷰 내의 링크를 클릭했을 때 뿐이라고 생각했었는데,

보여주는 HTML 내에 구글 지도 API가 있거나, ajax같은 요청을 할 때에도 http 프로토콜을 사용합니다.

그래서 navigationAction이 href 속성으로 링크를 여는 행위를 통한 Request인지 추가로 확인해야 합니다.

extension ViewController: WKNavigationDelegate {
  func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if 
      let url = navigationAction.request.url,
      let scheme = url.scheme,
      scheme == "https" || scheme == "http",
      navigationAcion.navigationType == .linkActivated,
      UIApplication.shared.canOpenURL(url) 
    {
      UIApplication.shared.open(url, options: [:], completionHandler: nil)
      decisionHandler(.cancel)
    } else {
      decisionHandler(.allow)
    }
  }
}