74

Within my app I am want to open links from within my domain (e.g.: communionchapelefca.org) in WKWebView but then have links from other domains open in Safari. I would prefer to do this programmatically.

I have found a few solutions on Stack overflow (here, here, here, and here) but they all seem to be Obj-C based and I am looking for a solution using Swift.

ViewController.swift:

import UIKit
import WebKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myWebView:WKWebView = WKWebView(frame: CGRectMake(0, 0,   UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
        
        myWebView.loadRequest(NSURLRequest(URL: NSURL(string: "http://www.communionchapelefca.org/app-home")!))
        
        self.view.addSubview(myWebView)
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Greg Williams
  • 1,109
  • 3
  • 10
  • 13

7 Answers7

160

You can implement WKNavigationDelegate, add the decidePolicyForNavigationAction method and check there the navigationType and requested url. I have used google.com below but you can just change it to your domain:

Xcode 8.3 • Swift 3.1 or later

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    let webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        webView.frame = view.bounds
        webView.navigationDelegate = self

        let url = URL(string: "https://www.google.com")!
        let urlRequest = URLRequest(url: url)

        webView.load(urlRequest)
        webView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        view.addSubview(webView)
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if navigationAction.navigationType == .linkActivated  {
            if let url = navigationAction.request.url,
                let host = url.host, !host.hasPrefix("www.google.com"),
                UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url)
                print(url)
                print("Redirected to browser. No need to open it locally")
                decisionHandler(.cancel)
                return
            } else {
                print("Open it locally")
                decisionHandler(.allow)
                return
            }
        } else {
            print("not a user click")
            decisionHandler(.allow)
            return
        }
    }
}
Saqib Omer
  • 5,387
  • 7
  • 50
  • 71
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 2
    this doesn't fully work for me, for example if you go to https://developer.xamarin.com/visual-studio-mac/ the devicePolicyForNavitationAction is called twice with that link, the first time with .LinkActivated and the second type with .Other, so I end up opening the browser but also navigating the webview. – matfillion Mar 30 '17 at 17:04
  • If i use open() it uses safari browser to open the url. I wanna open my url using a wkwebview inside my application. How do i achieve it. Anyone help. – iPhoneDeveloper Dec 22 '17 at 12:15
  • hi @LeoDabus, I have one URL and in that it just change a parameter of URL but this method unable to detect it. Here is that URL https://oporun.bubbleapps.io/version-test/appernaut. On button click, it changes just one parameter – Bhavin Bhadani May 10 '19 at 04:32
  • Leo, your usage of `google.com` might be misleading since that's exactly what he has used as an example of _other_ sites. Can you make an edit? – mfaani Feb 07 '20 at 04:26
  • 1
    The last sentence of my answer is clear about it IMO – Leo Dabus Feb 07 '20 at 04:30
  • this is not working at all. Might be an error in the logic – Khris Vandal Jan 27 '23 at 21:01
  • @KhrisVandal not working is too vague. Feel free to open a new question with a [mcve] – Leo Dabus Jan 27 '23 at 22:11
11

Here is sample code from the response to the swift written in obj c.

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler
{
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
        if (navigationAction.request.URL) {
            NSLog(@"%@", navigationAction.request.URL.host);
            if (![navigationAction.request.URL.resourceSpecifier containsString:@"ex path"]) { 
                if ([[UIApplication sharedApplication] canOpenURL:navigationAction.request.URL]) {
                    [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
                    decisionHandler(WKNavigationActionPolicyCancel);
                }
            } else {
                decisionHandler(WKNavigationActionPolicyAllow);
            }
        }
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}
headstream
  • 206
  • 2
  • 6
  • openURL with no other parameters is not deprecated. Use: [[UIApplication sharedApplication] openURL: navigationAction.request.URL options: @{} completionHandler: nil]; – Mike Aug 02 '20 at 22:47
9

For Swift 3.0

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    let wv = WKWebView(frame: UIScreen.main.bounds)
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let url =  NSURL(string: "https://www.google.com") else { return }
        wv.navigationDelegate = self
        wv.load(NSURLRequest(url: url as URL) as URLRequest)
        view.addSubview(wv)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if navigationAction.navigationType == .LinkActivated  {
            if let newURL = navigationAction.request.url,
                let host = newURL.host , !host.hasPrefix("www.google.com") &&
                UIApplication.shared.canOpenURL(newURL) &&
                UIApplication.shared.openURL(newURL) {
                    print(newURL)
                    print("Redirected to browser. No need to open it locally")
                    decisionHandler(.cancel)
            } else {
                print("Open it locally")
                decisionHandler(.allow)
            }
        } else {
            print("not a user click")
            decisionHandler(.allow)
        }
    }
}
Gianfranco Lemmo
  • 451
  • 5
  • 16
6

Swift 4 update for George Vardikos answer:

public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        let url = navigationAction.request.url
        guard url != nil else {
            decisionHandler(.allow)
            return
        }

        if url!.description.lowercased().starts(with: "http://") ||
            url!.description.lowercased().starts(with: "https://")  {
            decisionHandler(.cancel)
            UIApplication.shared.open(url!, options: [:], completionHandler: nil)
        } else {
            decisionHandler(.allow)
        }
}
Andy G
  • 129
  • 2
  • 4
  • Put the code above to your ViewController class and add WKUIDelegate to the class declaration: ` class ViewController: UIViewController, WKUIDelegate {` And add in viewDidLoad: mywebView.delegate = self – Andy G Apr 17 '20 at 06:47
3

Make a function to decide where to load the URL:

func loadURLString(str: String) {
    guard let url = NSURL(string: str) else {
        return
    }

    if url.host == "www.communionchapelefca.org" {
        // Open in myWebView
        myWebView.loadRequest(NSURLRequest(URL: url))
    } else {
        // Open in Safari
        UIApplication.sharedApplication().openURL(url)
    }
}

Usage:

loadURLString("http://www.communionchapelefca.org/app-home") // Open in myWebView
loadURLString("http://www.apple.com") // Open in Safari
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • sorry it took me so long to get back with you but your code didnt work when i click on an external link within my webview, it still opens it in webview. – Greg Williams Mar 28 '16 at 00:41
3

My swift 3 solution:

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

        let url = navigationAction.request.url

        if url?.description.lowercased().range(of: "http://") != nil || url?.description.lowercased().range(of: "https://") != nil {
            decisionHandler(.cancel)
            UIApplication.shared.openURL(url!)
        } else {
            decisionHandler(.allow)
        }

    }

Do not forget also to setup te delegate

    public override func loadView() {
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        view = webView
    }
George Vardikos
  • 2,345
  • 1
  • 14
  • 25
2

Swift 5 and iOS >10

Don't use UIApplication.shared.openURL(url!), it's deprecated. We use UIApplication.shared.open(url!) instead.

 public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        
        guard let url = navigationAction.request.url else{
            decisionHandler(.allow)
            return
        }
        
        let urlString = url.absoluteString.lowercased()
        if urlString.starts(with: "http://") || urlString.starts(with: "https://") {
            decisionHandler(.cancel)
            UIApplication.shared.open(url, options: [:])
        } else {
            decisionHandler(.allow)
        }
        
    }
winklerrr
  • 13,026
  • 8
  • 71
  • 88
ingconti
  • 10,876
  • 3
  • 61
  • 48
  • For some reason this method is not called in my delegate. All other methods in my `WKNavigationDelegate` are called, but not this one. Anyone knows what might be causing this? – Giel Berkers Dec 27 '21 at 07:46