Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WKWebView not opening custom URL scheme (js opens custom scheme link in new window)

I have a WKWebView in my application. I don't use UIWebView, because for some strange reason it doesn't open properly a web page with a lot of JS code in it.

When I tap on the link with custom url scheme "scm://", it does nothing...

My code:

- (void)viewDidLoad {
    // ...

    WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
    if ([configuration respondsToSelector:@selector(setDataDetectorTypes:)])
        [configuration setDataDetectorTypes:WKDataDetectorTypeLink];

    myWebView = [[WKWebView alloc] initWithFrame:webFrame configuration:configuration];
    myWebView.navigationDelegate = self;

    [self.view addSubview:myWebView];
}

#pragma mark - WKNavigationDelegate

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSURL *requestURL = navigationAction.request.URL;
    UIApplication *app = [UIApplication sharedApplication];
    if ([requestURL.scheme.lowercaseString isEqualToString:@"scm"] && [app canOpenURL:requestURL]) {
        [app openURL:requestURL];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else
        decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    [self handleError:error];
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    [self handleError:error];
}

#pragma mark - Handle web view errors.

- (void)handleError:(NSError *)error
{
    UIApplication *app = [UIApplication sharedApplication];
    app.networkActivityIndicatorVisible = NO;

    NSURL *failedUrl = error.userInfo[NSURLErrorFailingURLErrorKey];

    if ([failedUrl.scheme.lowercaseString isEqualToString:@"scm"]) {
        [app openURL:failedUrl];
    }
}

When I click custom url, handleError() is never called, neither decidePolicyForNavigationAction().

like image 896
Borzh Avatar asked Oct 31 '17 16:10

Borzh


4 Answers

Ok, figured this out... happens that the link was opening in new window, so adding next code along with setting UIDelegate made it work

    // Somewhere in viewDidLoad()...
    myWebView.UIDelegate = self;
}

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
    NSLog(@"createWebViewWithConfiguration %@ %@", navigationAction, windowFeatures);
    if (!navigationAction.targetFrame.isMainFrame) {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        [(WKWebView *)_webView loadRequest:navigationAction.request];
    }
    return nil;
}
like image 105
Borzh Avatar answered Oct 16 '22 21:10

Borzh


-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

    NSURLRequest *request = navigationAction.request;
    if(![request.URL.absoluteString hasPrefix:@"http://"] && ![request.URL.absoluteString hasPrefix:@"https://"]) {
        if([[UIApplication sharedApplication] canOpenURL:request.URL]) {
            //urlscheme, tel, mailto, etc.
            [[UIApplication sharedApplication] openURL:request.URL];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

Note: This answer is just focus on urlscheme, but maybe lead to other problems. Thanks for your feedback!

like image 25
hstdt Avatar answered Oct 16 '22 21:10

hstdt


The following works in Swift 5.1 on iOS 13.1.3 (variation of @hstdt's answer) for WKWebView handling minimally the following (tested) schemas: sms:, tel:, and mailto:.

Add the following to whereever you're setting up your WKWebView.

// assign the delegate
webView.navigationDelegate = self

Then, add the following function somewhere in your class.

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

    // if the request is a non-http(s) schema, then have the UIApplication handle
    // opening the request
    if let url = navigationAction.request.url,
        !url.absoluteString.hasPrefix("http://"),
        !url.absoluteString.hasPrefix("https://"),
        UIApplication.shared.canOpenURL(url) {

        // have UIApplication handle the url (sms:, tel:, mailto:, ...)
        UIApplication.shared.open(url, options: [:], completionHandler: nil)

        // cancel the request (handled by UIApplication)
        decisionHandler(.cancel)
    }
    else {
        // allow the request
        decisionHandler(.allow)
    }
}

Explanation:

  • WKWebView seems to be unequipped to handle non-http(s) url schemas.
  • The above code catches the request before WKWebView has a chance to render/load it to check for a different schema.
  • We check for a non-http(s) schema and have the UIApplication handle it instead.

Note: Comment if you find other schemas working or not working using this solution.

Again, this solution was sourced and modified from @hstdt's answer.

like image 27
mattblessed Avatar answered Oct 16 '22 22:10

mattblessed


Solution for Swift 5: The following is a variation of mattblessed's answer for WKWebView which works for all application URL-Schemes not only the system URL-Schmes like "sms:", "tel:", and "mailto:"

This solution will work without adding custom URL Schemes to your apps plist file via LSApplicationQueriesSchemes. (Which is necessary since iOS 10 - for detail see this article: canOpenURL not working in ios 10)

Add the following implementation of WKNavigationDelegate to your class:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    if let requestUrl = navigationAction.request.url, requestUrl.isCustomUrlScheme() {
        decisionHandler(.cancel)

        // try to open URLs like e.g. "whatsapp://"
        UIApplication.shared.open(requestUrl, options: [:]) { success in
            if !success {
                //TODO 
                // add your code for handling URLs that can't be opened here - 
                // maybe show an error alert
            }
        }
    } else {
        // allow the request
        decisionHandler(.allow)
    }
}

Add the following Extension of URL to your class:

extension URL {

    func isCustomUrlScheme() -> Bool {
        let webUrlPrefixes = ["http://", "https://", "about:"]

        let urlStringLowerCase = self.absoluteString.lowercased()
        for webUrlPrefix in webUrlPrefixes {
            if urlStringLowerCase.hasPrefix(webUrlPrefix) {
            return false
        }

        return urlStringLowerCase.contains(":")
    }
}
like image 5
Jochen Holzer Avatar answered Oct 16 '22 21:10

Jochen Holzer