딱히 브릿지로 약속을 하지 않았다면 웹뷰 개발자가 프로덕션에서 충분히 쓸법한 메소드인데, 따로 구현을 안해주면 WKWebView에서는 기본동작을 즉시 확인버튼을 누른것처럼 동작한다고 한다. ~~~문서가 objc로 연결되네…~~~
/*! @abstract Displays a JavaScript alert panel. @param webView The web view invoking the delegate method. @param message The message to display. @param frame Information about the frame whose JavaScript initiated this call. @param completionHandler The completion handler to call after the alert panel has been dismissed. @discussion For user security, your app should call attention to the fact that a specific website controls the content in this panel. A simple forumla for identifying the controlling website is frame.request.URL.host. The panel should have a single OK button. If you do not implement this method, the web view will behave as if the user selected the OK button. */- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(WK_SWIFT_UI_ACTOR void (^)(void))completionHandler;
이를 해결하려면 WKUIDelegate를 채택하고 구현해주면 된다.비슷한 맥락으로 window.confirm()
, window.prompt() 도 같이 구현할 수 있다.
웹브라우저에서는 타이틀에 어떤 윈도우에서 알럿창이 열렸는지 주소가 적히는데, 웹뷰에서 주소 보여주기도 이상하기 때문에 적당히 타이틀과 메시지를 설정해주면 된다.
웹뷰에서 앱스킴을 가지고 있는 경우, 직접 링크의 스킴을 검사해 해당 앱스킴으로 이동하고 싶은 경우 처리한다. 이때 스킴 자체는 당연히 Info.plist의 LSApplicationQueriesSchemes에 등록되어 있어야 한다.
func webView( _ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if let url = navigationAction.request.url, url.scheme != "http" && url.scheme != "https" { defer { decisionHandler(.cancel) } guard UIApplication.shared.canOpenURL(url) else { /// 에러처리 } UIApplication.shared.open(url) { success in if !success { /// 에러처리 } } return } /// 이후 동작 처리 -> decisionHandler(.allow)}
새 창 열기
웹뷰 내 컨텐츠에서 a태그를 이용해 하이퍼링크를 걸고, target="_blank"를잡으면, webView(_:decidePolicyFor:decisionHandler:)에서 WKNavigationAction의 navigationType이 linkActivated로 콜백이 떨어지는데, targetFrame 이 없으면 이 동작이 기본적으로 꺼져있어 링크가 열리지 않기 때문에 처리를 해 줘야 한다.
사파리같은 웹브라우저에서는 탭이 있으니 새 탭으로 열리는데, 웹앱 특성상 보통 탭이 없으니 그냥 현재 webView에 url를 로드시켜버리는 방법이 많이 쓰인다.
그리고 웹에서 window.open()을 실행시키는 경우 webView(_:createWebViewWith:for:windowFeatures:)에서 처리하면 된다. webView(_:decidePolicyFor:decisionHandler:)에서 decisionHandler를 allow하면 이곳으로 떨어진다.
WKWebView는 앱과 다른 프로세스로 실행된다. 따라서 WKWebView만 단독으로 킬을 맞을 수 있는데(보통은 메모리 이슈) 이때 webViewWebContentProcessDidTerminate가 불리게 된다. 처리하지 않으면 웹뷰만 죽어버리기 때문에 리로드시켜주면 된다.
만약 서비스가 iPad를 지원하고 있다면, 웹뷰 개발자는 보통 Request Header의 User-Agent로 분기를 칠 텐데, iOS 13.0(iPadOS의 독립)부터 User-Agent가아이패드가 아닌 맥으로 찍히기 때문에 웹뷰 개발자의 기분이 안 좋을 것이다. 최대 터치포인트 개수를 세서 분기를 친다던데…
웹뷰 config를 바꿔주면 이를 방지할 수 있다. 만약 기본 동작을 유지하고 직접 커스텀하고싶으면 UIDevice.current.userInterfaceIdiom == .pad로 분기쳐서 직접 세팅해주면 된다.
extension WKWebViewConfiguration { static let defaultConfig: WKWebViewConfiguration = { let config = WKWebViewConfiguration() if #available(iOS 13.0, *) { let preference = WKWebpagePreferences() preference.preferredContentMode = .mobile config.defaultWebpagePreferences = preference } return config }()}let webView = WKWebView(frame: .zero, configuration: .defaultConfig)