WKWebView: mailto links in html content not opening mail app
Asked Answered
B

1

15

I created a very simple iOS app (Swift 5). It's just a WKWebView that loads my PWA url.

Everything works fine except all <a href="mailto:[email protected]">Mail me</a> links. When I click them, nothing happens, my mail app doesn't open.

This is the code of my ViewController.swift:

//
//  ViewController.swift
//  panel
//
//  Created by kevin on 25/07/2019.
//  Copyright © 2019 umono. All rights reserved.
//

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate {
    
    var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myURL = URL(string:"https://someUrlToMyApp.appspot.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
        
        if #available(iOS 11.0, *) {
            webView.scrollView.contentInsetAdjustmentBehavior = .never;
        }
        
    }
    
    override func loadView() {
        
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.dataDetectorTypes = [.all]
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view = webView
        
    }

}

EDIT:

Thx guy's, here is my working code:

//
//  ViewController.swift
//  panel
//
//  Created by kevin on 25/07/2019.
//  Copyright © 2019 umono. All rights reserved.
//

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate {
    
    var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myURL = URL(string:"https://someUrlToMyApp.appspot.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
        
        if #available(iOS 11.0, *) {
            webView.scrollView.contentInsetAdjustmentBehavior = .never;
        }
        
        webView.navigationDelegate = self
        
    }
    
    override func loadView() {
        
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.dataDetectorTypes = [.all]
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view = webView
        
    }

}

extension ViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        guard
            let url = navigationAction.request.url else {
                decisionHandler(.cancel)
                return
        }
        
        let string = url.absoluteString
        if (string.contains("mailto:")) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
            decisionHandler(.cancel)

            return
        }
        decisionHandler(.allow)
    }
}
Beryl answered 25/7, 2019 at 11:31 Comment(10)
Is mail setup in device settings?Lovelace
are you on a simulator or on a real device?Potpourri
Do you mean in my own iphone device? Or is this a setting in my IOS app project?Beryl
I'm on a real deviceBeryl
@Beryl In real device open this page in safari and click on Mail Me. If it does not open the email composer then go to device settings and check if mail is configured or not.Lovelace
It works in safari (it opens my email app)Beryl
maybe this is the same problem #26501672Potpourri
I tried everything that is offered as a solution in that post, nothing worked...Beryl
Set WKWebViewConfiguration's dataDetectorTypes = WKDataDetectorTypeAll; then it should work without overriding the web view method. You can check this answer for how to set the configuration.Lovelace
@Lovelace as you can see in my code above, i already set that: webConfiguration.dataDetectorTypes = [.all]Beryl
P
17

One way to do what you want would be to implement WKNavigationDelegate:

import UIKit
import WebKit

class ViewController: UIViewController {

    @IBOutlet weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        guard
            let file = Bundle.main.path(forResource: "test", ofType: "html"),
            let html = try? String(contentsOfFile: file) else {
                return
        }

        webView.navigationDelegate = self
        webView.loadHTMLString(html, baseURL: nil)
    }

    @IBAction func didTapButton(_ sender: Any) {
        let email = "[email protected]"
        guard
            let url = URL(string: "mailto:\(email)") else {
                return
        }

        UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
}

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

        if (scheme.lowercased() == "mailto") {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
            // here I decide to .cancel, do as you wish
            decisionHandler(.cancel)
            return
        }
        decisionHandler(.allow)
    }
}

Here you have a ViewController that has webView as an outlet, this WKWebView would load an html file like this:

<a href="mailto:[email protected]">Mail me</a>

And I also added in storyboard a button just for reference, which would have the IBAction didTapButton described above.

The key here is:

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

Which would give you the URL and let you decide what policy is suitable for it. Here I check if it contains mailto: as I already know this is what you're interested in so if it does, I simply open the URL as I would do if the user presses an UIButton visible on screen.

Hope it helps, cheers!

LE: Make sure you run on a real device (simulators don't have Mail app installed), also make sure you have the Mail app installed, cause I didn't..

Papillon answered 25/7, 2019 at 12:21 Comment(7)
Hi, thx... i tried to add your code (func webView) however, this doesn't change anything. I also added a print('test') below if (string.contains("mailto:")) to see if the code executes when i click the mailto link, but nothing prints in the console.Beryl
@Beryl Did you set webView.navigationDelegate = self ?Lovelace
Updated my original post to include the full working solutionBeryl
But this method will fail if your url contains mailto string anywhere in the url string. for example: https://abcmailto.com, it will open it outside the app according to condition. So better to match url.scheme instead of contains function.Lovelace
@Lovelace you are right, however @Beryl should decide what policy should do after the Mail app is gonna open, I decided to .cancel.Ludvig
What about if you're using a JavaScript function because you're doing some logging and conditional building of a subject and body, then calling window.open(``mailto:[email protected]?subject=${subject}&body=${body}``, '_blank'); This does not appear to hit the delegate method.Wanton
Found this to be a semi elegant solution so I didn't have to go and modify all of my web code to use hrefs where applicable: blog.nextzy.me/…Wanton

© 2022 - 2024 — McMap. All rights reserved.