WKWebViewでtarget="_blank"なリンクが開かない時の対処法


普通にWKWebViewを実装したらtarget="_blank"なリンクが押されたときに無反応になるので対処してみました。別の回避方法がありましたらコメント欄にて教えて下さい。

ついでにアプリリンク(iTunes)や他アプリへのURL Schemeの対応も書いています。

(※ `WKNavigationDelegate`プロトコルと`webView.navigationDelegate = self`とすることをお忘れなく。)


# Swift 3.0版

```swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    
    guard let url = navigationAction.request.url else {
        decisionHandler(.cancel)
        return
    }
    
    if url.absoluteString.range(of: "//itunes.apple.com/") != nil {
        if UIApplication.shared.responds(to: #selector(UIApplication.open(_:options:completionHandler:))) {
            UIApplication.shared.open(url, options: [UIApplicationOpenURLOptionUniversalLinksOnly:false], completionHandler: { (finished: Bool) in
            })
        }
        else {
            // iOS 10 で deprecated 必要なら以降のopenURLも振り分ける
            // iOS 10以降は UIApplication.shared.open(url, options: [:], completionHandler: nil)
            UIApplication.shared.openURL(url)
        }
        decisionHandler(.cancel)
        return
    }
    else if !url.absoluteString.hasPrefix("http://")
        && !url.absoluteString.hasPrefix("https://") {
        // URL Schemeをinfo.plistで公開しているアプリか確認
        if UIApplication.shared.canOpenURL(url) {
            UIApplication.shared.openURL(url)
            decisionHandler(.cancel)
            return
        }
        //                // 確認せずとりあえず開く
        //                UIApplication.shared.openURL(url)
        //                decisionHandler(.cancel)
        //                return
    }
    
    switch navigationAction.navigationType {
    case .linkActivated:
        if navigationAction.targetFrame == nil
            || !navigationAction.targetFrame!.isMainFrame {
            // <a href="..." target="_blank"> が押されたとき
            webView.load(URLRequest(url: url))
            decisionHandler(.cancel)
            return
        }
    case .backForward:
        break
    case .formResubmitted:
        break
    case .formSubmitted:
        break
    case .other:
        break
    case .reload:
        break
    } // 全要素列挙した場合はdefault不要 (足りない要素が追加されたときにエラーを吐かせる目的)
    
    decisionHandler(.allow)
}
```

## 別法

コメント欄に @taketo1024 さんが書いてくださった方法のSwift 3.0版です。
(※ 上記はプロトコル`WKNavigationDelegate`と`webView.navigationDelegate = self`を指定することで動きますが、こちらは`WKUIDelegate`と`webView.uiDelegate = self`と指定します。)

```swift
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    guard let url = navigationAction.request.url else {
        return nil
    }
    
    guard let targetFrame = navigationAction.targetFrame, targetFrame.isMainFrame else {
        webView.load(URLRequest(url: url))
        return nil
    }
    return nil
}
```


# Objective-C

```objc
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSURL *url = navigationAction.request.URL;
    
    // iTunes: アプリのリンクなど
    if ([url.absoluteString rangeOfString:@"//itunes.apple.com/"].location != NSNotFound) {
        [[UIApplication sharedApplication] openURL:url];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    // http(s) 以外のプロトコル: 他のアプリへ移動する場合
    else if (![url.absoluteString hasPrefix:@"http://"]
             && [url.absoluteString hasPrefix:@"https://"]) {
        // URL Schemeをinfo.plistで公開しているかを確認
        // if ([[UIApplication sharedApplication] canOpenURL:url]]) {
        //     [[UIApplication sharedApplication] openURL:url];
        //     decisionHandler(WKNavigationActionPolicyCancel);
        // }
        // or 確認せず開く
        [[UIApplication sharedApplication] openURL:url];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    
    switch (navigationAction.navigationType) {
        case WKNavigationTypeLinkActivated: {
            NSLog(@"WKNavigationTypeLinkActivated::");
            // <a href="..." target="_blank"> が押されたとき
            if (!navigationAction.targetFrame
                || !navigationAction.targetFrame.isMainFrame) {
                [webView loadRequest:[NSURLRequest requestWithURL:url]];
                decisionHandler(WKNavigationActionPolicyCancel);
                return;
            }
            break;
        }
        case WKNavigationTypeFormSubmitted: {
            NSLog(@"WKNavigationTypeFormSubmitted::");
            break;
        }
        case WKNavigationTypeBackForward: {
            NSLog(@"WKNavigationTypeBackForward::");
            break;
        }
        case WKNavigationTypeReload: {
            NSLog(@"WKNavigationTypeReload::");
            break;
        }
        case WKNavigationTypeFormResubmitted: {
            NSLog(@"WKNavigationTypeFormResubmitted::");
            break;
        }
        case WKNavigationTypeOther: {
            NSLog(@"WKNavigationTypeOther::");
            break;
        }
    } // 全要素列挙した場合はdefault不要 (足りない要素が追加されたときにエラーを吐かせる目的)
    
    decisionHandler(WKNavigationActionPolicyAllow);
}
```