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)
}
}
}