8

I've looked though a lot of the answers for this question but they seem to be outdated now and none of the solutions are working for me and just give lots of errors.

I'm just gettign into xcode and apps and am loading a local html file "index.html" into a WKWebView. This is fine, loads fine, displays as it should, BUT I have some tel: links on the page which don't work, I tap on them and nothing. I am using fraework7 for my html files and have added the "external" class, which works in safari etc.

UPDATE: Using the following code I can click on a link and it will try and call, but then gives a fatal error

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate{

    @IBOutlet weak var webview: WKWebView!
    override func viewDidLoad() {
        webview.navigationDelegate = self
        super.viewDidLoad()
        let htmlpath = Bundle.main.path(forResource: "index", ofType: "html")
        let url = URL(fileURLWithPath: htmlpath!)
        let request = URLRequest(url: url)
        webview.load(request)
        webview.scrollView.bounces = false
        webview.configuration.dataDetectorTypes = .phoneNumber
        // Do any additional setup after loading the view, typically from a nib.
    }
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
        if navigationAction.request.url?.scheme == "tel" {
            UIApplication.shared.openURL(navigationAction.request.url!)
            decisionHandler(.cancel)
        }
        decisionHandler(.allow)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

and html looks like:

          <tr>

            <td>Police / Fire / Ambulance</td>

            <td>Emergency Services</td>

            <td><a href = "tel:999" class = "external">999</a></td>

          </tr>

obviously part of a larger table but this is the example.

Any pointers would be greatly appreciated as I've been going round in circles for ages.

Cerbster
  • 213
  • 2
  • 8
  • UPDATE: I have tried the following code and it works... once then I get a "Thread 1:signal SIGABRT" error when the link is clicked. – Cerbster Mar 05 '18 at 13:12
  • 2
    Possible duplicate of [Launching phone/email/map links in WKWebView](https://stackoverflow.com/questions/26501172/launching-phone-email-map-links-in-wkwebview) – Agent Smith Mar 05 '18 at 13:13

4 Answers4

12

Managed to find a solution finally:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate{

  @IBOutlet weak var webView: WKWebView!

  override func viewDidLoad() {
    webView.navigationDelegate = self
    webView.uiDelegate = self
    super.viewDidLoad()
    let htmlpath = Bundle.main.path(forResource: "index", ofType: "html")
    let url2 = URL(fileURLWithPath: htmlpath!)
    let request = URLRequest(url: url2)
    webView.load(request)
    webView.scrollView.bounces = false
    //webview.configuration.dataDetectorTypes = .phoneNumber
  }

  func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if navigationAction.request.url?.scheme == "tel" {
        UIApplication.shared.openURL(navigationAction.request.url!)
        decisionHandler(.cancel)
    } else {
        decisionHandler(.allow)
    }
}

Using this seems to pick up the tel: links fine, an answer I found on here previously didn't have the else statement so fired decisionHandler more than once which created the error, the else statement fixed that and now seems fine.

wjl
  • 7,143
  • 1
  • 30
  • 49
Cerbster
  • 213
  • 2
  • 8
8

It's not just tel: links that aren't handled out of the box by WKWebView. Other links like mailto: and facetime: aren't handled either. For a complete list of these kinds of special links, see Apple's documentation.

You should decide which of Apple's special links you want handled (see the link above), and override the navigation handler in a manner similar to the following:

webView.navigationDelegate = self

...

extension ViewController: WKNavigationDelegate {

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

    if ["tel", "sms", "facetime"].contains(url.scheme) && UIApplication.shared.canOpenURL(url) {
      UIApplication.shared.open(url, options: [:], completionHandler: nil)
      decisionHandler(.cancel)
    } else {
      decisionHandler(.allow)
    }
  }
}

The only reason I can think of why Apple didn't include this in the first place is because when you try launching one of these links in Mobile Safari, it shows you a dialog such as the following:

This website has been blocked from automatically starting a call.
[Ignore] [Allow Call]

Thus, they may expect you want to implement something similar in your app instead of just launching the call.

Note however, that each type of link behaves differently:

  • tel: dialog that shows the phone number with call/cancel buttons.
  • sms: launches the Messages app.
  • mailto: launches the Mail app.

So if you do want to have some kind of modal that allows the user to cancel if they didn't mean to perform the action, depending on which actions you support it might already have similar behavior (e.g. tel:).

Senseful
  • 86,719
  • 67
  • 308
  • 465
1

Just use WKNavigationDelegate and dataDetectorTypes for WKWebView configuration like below:

webView.navigationDelegate = self
webView.configuration.dataDetectorTypes = [.link, .phoneNumber]

extension PDFWebViewController: WKNavigationDelegate {

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if let requestUrl = navigationAction.request.url, requestUrl.scheme == "tel" {
            UIApplication.shared.open(requestUrl, options: [:], completionHandler: nil)
            decisionHandler(.cancel)

        } else {
            decisionHandler(.allow)
        }
    }

}
birdy
  • 943
  • 13
  • 25
0

While suggested answers work fine - I would recommend to use white-list approach instead of black-list. We know that WKWebView can only handle http/https links so we can gracefully handle the error of opening any other kind of link.

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

func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
    self.handleNavigationError(error)
}

private func handleNavigationError(_ error: Error) {
    if let failedUrl = (error as NSError).userInfo[NSURLErrorFailingURLErrorKey] as? URL, failedUrlScheme = failedUrl.scheme?.lowercased(), !["http", "https"].contains(failedUrlScheme) {
        UIApplication.shared.open(failedUrl, completionHandler: nil)
    } else {
        // handle other errors if needed
    }
}

In this case you will support all the types of external app links like tel:, sms:, faceTime:, itms-services: etc.