Search code examples
iosobjective-cuiwebview

UIWebView - force navigation in safari


I have a UIWebView in objective-c that loads an external HTML with an embedded video (I have no access to this HTML). This video has a pre-roll ad from Google Ads (but can be from another provider in the future). This ad has a link to an external website that the user can click but it seems to be triggered by a javascript event (not a regular anchor).

I have set the delegate in order to force clicked links in the webview to open in Safari, but these links from the ads keep opening inside the webview. I think it's because they are triggered from javascript.

This is my delegate:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:
(NSURLRequest *)request navigationType:
(UIWebViewNavigationType)navigationType
{
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        [[UIApplication sharedApplication] openURL:[request URL]];
        return NO;
    }
return YES;
}

Does anyone knows of a way to force any navigation outside the domain loaded in the webview to open in Safari? I guess this way I could circumvent this problem.

Thanks.


Solution

  • Assuming you know the "internal" domain ahead of time, you could force all external domains to open in Safari:

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    
        if (![request.url.absoluteString containsString:@"https://www.yourinternaldomain.com"]) {
            [[UIApplication sharedApplication] openURL:[request URL]];
            return NO;
        }
    
        return YES;
    }
    

    Update:

    Based on your comments, if the above won't suffice, you could add a UITapGestureRecognizer to detect user input:

    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
    tapGesture.numberOfTouchesRequired = 1;
    tapGesture.numberOfTapsRequired = 1;
    tapGesture.delegate = self;
    [self.webView addGestureRecognizer:tapGesture];
    

    Implement the delegate methods to ensure your tap is recognised:

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
        return YES;
    }
    

    In your -tapGesture: method, you can then set a temporary BOOL:

    -(void)tapGesture:(UITapGestureRecognizer *)tapGesture {
        self.userDidTap = YES;
    }
    

    And then in the subsequent -webView:shouldStartLoadWithRequest:navigationType: method, you can inspect the self.userDidTap value and act accordingly:

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    
        if (self.userDidTap) {
            [[UIApplication sharedApplication] openURL:[request URL]];
            return NO;
        }
    
        return YES;
    }