Jed Grant
Jed Grant

Reputation: 1425

Open a WKWebview target="_blank" link in Safari

I am trying to get my Hybrid IOS app that uses Swift and WKWebviews to open a link that has target="_blank" or if the URL contains http://, https://, or mailto: in Mobile Safari.

From this answer I get this code.

func webView(webView: WKWebView!, createWebViewWithConfiguration     configuration: WKWebViewConfiguration!, forNavigationAction navigationAction:     WKNavigationAction!, windowFeatures: WKWindowFeatures!) -> WKWebView! {
    if navigationAction.targetFrame == nil {
        webView.loadRequest(navigationAction.request)
    }
    return nil
}

First, that doesn't do anything for me. Second, I want it to open in a new window. And I found this code that is supposed to do something like that...

if let requestUrl = NSURL(string: "http://www.iSecurityPlus.com") {
     UIApplication.sharedApplication().openURL(requestUrl)
}

How do I put these two together and get them to work? What do I need to add to the ViewController declaration to make it work?

Upvotes: 12

Views: 26375

Answers (6)

jose920405
jose920405

Reputation: 8049

First add WKNavigationDelegate and webviewWk.navigationDelegate = self

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
            
// This is a 'new window action' (aka target="_blank") > open this URL externally. 
// If we´re doing nothing here, WKWebView will also just do nothing. 
// Maybe this will change in a later stage of the iOS 8 Beta
       if navigationAction.navigationType == WKNavigationType.linkActivated {
           if let url = navigationAction.request.url, UIApplication.shared.canOpenURL(url) {
               print("Trying to open \(url.absoluteString)")
               UIApplication.shared.open(url)
            }
       }
       decisionHandler(WKNavigationActionPolicy.allow)
}

Upvotes: 13

Shawn Frank
Shawn Frank

Reputation: 5143

While some of the above solutions worked to an extent, the issue was that all the links started to open externally not just the ones where the HTTPs url scheme was not followed like mailto, blank, deeplinks etc.

I implement these WKNavigationDelegate functions

  1. decidePolicyFor navigationAction (documentation link) to allow even urls that do not follow the HTTPs scheme to be allowed to be processed

  2. this navigation fail delegate function webView didFailProvisionalNavigation and check if iOS can handle the open in a new tab, mail, deep link etc so in your case it would open the app store

  3. You could also implement the same logic as point 2 in this WKNavigationDelegate function just in case

Here is the code:

class ViewController: UIViewController, WKNavigationDelegate
{
    // Initialize the webview anywhere you wish
    override func viewDidAppear(_ animated: Bool)
    {
        super.viewDidAppear(animated)
        
        let html = """
        <a href="https://apps.apple.com/us/app/directorio-notarios-cdmx/id1544000342"> App store Deep link usually doesn't open </a></span></span><br />

        <a href="https://landercorp.mx" rel="noopener"> Normal link </a></span></span><br />
        """
        
        let webview = WKWebView()
        webview.frame = view.bounds
        webview.navigationDelegate = self
        view.addSubview(webview)
        webview.loadHTMLString(html, baseURL: nil)
    }
    
    // MARK: WKNavigationDelegates
    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
    {
        decisionHandler(.allow)
    }
    
    
    func webView(_ webView: WKWebView,
                 didFailProvisionalNavigation navigation: WKNavigation!,
                 withError error: Error)
    {
        manageFailedNavigation(webView,
                               didFail: navigation,
                               withError: error)
    }

    func webView(_ webView: WKWebView,
                 didFail navigation: WKNavigation!,
                 withError error: Error)
    {
        manageFailedNavigation(webView,
                               didFail: navigation,
                               withError: error)
    }

    private func manageFailedNavigation(_ webView: WKWebView,
                                        didFail navigation: WKNavigation!,
                                        withError error: Error)
    {
        // Check if this failed because of mailto, _blank, deep links etc
        // I have commented out how to check for a specific case like open in a new tab,
        // you can try to handle each case as you wish
        if error.localizedDescription
            == "Redirection to URL with a scheme that is not HTTP(S)"
           //let url = webView.url, url.description.lowercased().range(of: "blank") != nil
        {
            // Convert error to NSError so we can access the url
            let nsError = error as NSError
            
            // Get the url from the error
            // This key could change in future iOS releases
            if let failedURL = nsError.userInfo["NSErrorFailingURLKey"] as? URL
            {
                // Check if the action can be handled by iOS
                if UIApplication.shared.canOpenURL(failedURL)
                {
                    // Request iOS to open handle the link
                    UIApplication.shared.open(failedURL, options: [:],
                                              completionHandler: nil)
                }
            }
        }
    }
}

This way, both cases are handled where the HTTPs url scheme is followed and not

Open WKWebview target="_blank" mailto deeplink new tab app store link

Upvotes: 0

Daniel
Daniel

Reputation: 1037

Swift 4.2

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
    if navigationAction.navigationType == WKNavigationType.linkActivated {
        print("here link Activated!!!")
        if let url = navigationAction.request.url {
            let shared = UIApplication.shared
            if shared.canOpenURL(url) {
                shared.open(url, options: [:], completionHandler: nil)
            }
        }
        decisionHandler(.cancel)
    }
    else {
        decisionHandler(.allow)
    }
}

Upvotes: 4

ltrainpr
ltrainpr

Reputation: 3463

Code updated for iOS 10 Swift 3:

override func loadView() {
    super.loadView()
    self.webView.navigationDelegate = self 
    self.webView.uiDelegate = self  //must have this
}

func webView(_ webView: WKWebView,
               createWebViewWith configuration: WKWebViewConfiguration,
               for navigationAction: WKNavigationAction,
               windowFeatures: WKWindowFeatures) -> WKWebView? {
    if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
      if url.description.lowercased().range(of: "http://") != nil ||
        url.description.lowercased().range(of: "https://") != nil ||
        url.description.lowercased().range(of: "mailto:") != nil {
        UIApplication.shared.openURL(url)
      }
    }
  return nil
}

Upvotes: 13

hojin
hojin

Reputation: 1395

func webView(_ webView: WKWebView,
           createWebViewWith configuration: WKWebViewConfiguration,
           for navigationAction: WKNavigationAction,
           windowFeatures: WKWindowFeatures) -> WKWebView? {
  if navigationAction.targetFrame == nil, let url = navigationAction.request.url, let scheme = url.scheme {
    if ["http", "https", "mailto"].contains(where: { $0.caseInsensitiveCompare(scheme) == .orderedSame }) {
      UIApplication.shared.openURL(url)
    }
  }
  return nil
}

Upvotes: 2

Jed Grant
Jed Grant

Reputation: 1425

In (from here)

 override func loadView() {
    super.loadView()
    self.webView.navigationDelegate = self 
    self.webView.UIDelegate = self  //must have this
 }

Then add the function (from here, with additions)...

func webView(webView: WKWebView,
    createWebViewWithConfiguration configuration: WKWebViewConfiguration,
    forNavigationAction navigationAction: WKNavigationAction,
    windowFeatures: WKWindowFeatures) -> WKWebView? {
        if navigationAction.targetFrame == nil {
            var url = navigationAction.request.URL
            if url.description.lowercaseString.rangeOfString("http://") != nil || url.description.lowercaseString.rangeOfString("https://") != nil || url.description.lowercaseString.rangeOfString("mailto:") != nil  {
                UIApplication.sharedApplication().openURL(url)
            }
        }
        return nil
}

Upvotes: 13

Related Questions