2

I am loading url in webView and now I want to block ads from url in webView. How can I achieve this?

[_webVw loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:page_url]]];
Lucio
  • 4,753
  • 3
  • 48
  • 77
Shah Paneri
  • 729
  • 7
  • 28

2 Answers2

5

You need to use the webView:shouldStartLoadWithRequest:navigationType: method from UIWebViewDelegate and do your validation with black list of URL to block. If you want an inspiration or similar process you can see this library Ad Blocker iOS

--- EDIT ---

This is my code to Ads Blocking WKWebView from NSString.

//
//  ViewController.m
//  WKWebView
//
//  Created by Carlos Landaverde on 6/21/18.
//

#import "ViewController.h"

#define NSStringMultiline(...) [[NSString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]

static NSString *rexKey1 = @"rexKey1";

@interface ViewController () <WKNavigationDelegate, WKUIDelegate>
@property (nonatomic) WKWebView *webView;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.view setBackgroundColor: [UIColor whiteColor]];

    dispatch_group_t dispatchGroup = dispatch_group_create();
    __weak typeof(self) weakSelf = self;

    [NSUserDefaults.standardUserDefaults registerDefaults: @{ rexKey1: @NO }];
    [NSUserDefaults.standardUserDefaults synchronize];

    _webView = [[WKWebView alloc] initWithFrame: self.view.bounds];
    [_webView setNavigationDelegate: self];
    [_webView setUIDelegate: self];
    [_webView setAllowsBackForwardNavigationGestures: YES];
    [self.view addSubview: _webView];

    // If you want to remove previous WKContentRuleList
    // [_webView.configuration.userContentController removeAllContentRuleLists];

    dispatch_group_enter(dispatchGroup);
    [self setupContentBlockFromStringLiteral: ^{
        dispatch_group_leave(dispatchGroup);
    }];

    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
        [weakSelf beginLoading];
    });
}

- (void)beginLoading
{
    NSURLRequest *urlReq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: @"https://www.google.com"]];
    [_webView loadRequest: urlReq];
}

- (void)setupContentBlockFromStringLiteral: (void (^)(void))callback
{
    /*
     Or you can block from a JSON file
     */
    NSString *jsonStr = NSStringMultiline([{
        "trigger": {
            "url-filter": "://googleads\\\\.g\\\\.doubleclick\\\\.net.*"
        },
        "action": {
            "type": "block"
        }
    }]);

    if ([[NSUserDefaults.standardUserDefaults objectForKey: rexKey1] boolValue]) {
        [WKContentRuleListStore.defaultStore compileContentRuleListForIdentifier: rexKey1 encodedContentRuleList: jsonStr completionHandler: ^(WKContentRuleList *contentRuleList, NSError *err) {

            if (err != nil) {
                NSLog(@"Error on content rule list compiled");
                [NSUserDefaults.standardUserDefaults setObject: @NO forKey: rexKey1];
                return;
            }

            if (contentRuleList) {
                [_webView.configuration.userContentController addContentRuleList: contentRuleList];
                callback();
            }
        }];
    }
    else {
        [WKContentRuleListStore.defaultStore compileContentRuleListForIdentifier: rexKey1 encodedContentRuleList: jsonStr completionHandler: ^(WKContentRuleList *contentRuleList, NSError *err) {

            if (err != nil) {
                NSLog(@"Error on content rule list not compiled");
            }

            if (contentRuleList) {
                [_webView.configuration.userContentController addContentRuleList: contentRuleList];
                [NSUserDefaults.standardUserDefaults setObject: @YES forKey: rexKey1];
                callback();
            }
        }];
    }
}

@end
Ladd.c
  • 973
  • 11
  • 11
  • I put some code using WKContentRuleListStore to ads blocking from NSString cheers :) – Ladd.c Jun 21 '18 at 16:18
  • Code you have posted is not working. I've loaded this url `https://www.ndtv.com/tamil-nadu-news/tuticorin-stop-acting-cop-told-wounded-sterlite-protester-who-died-in-hospital-1856712?pfrom=home-topscroll`. Can you please test using this url ? – Shah Paneri Jun 22 '18 at 05:17
  • The code is working very well, that web site has many ads URL, you need to do a black list JSON file to block all URLs check this https://github.com/AdAway/AdAway/wiki/HostsSources – Ladd.c Jun 22 '18 at 14:40
  • I've tried to block ads using adaway.json file but still ads not blocked.. Can you please try using json file and let me know if it's working or not?? – Shah Paneri Jul 05 '18 at 10:44
  • https://17fae754bfa92173f597c0f0b05a57aa.safeframe.googlesyndication.com/safeframe/1-0-38/html/container.html. i want to block this kind of url then what should be the regular expression for that in "url-filter" ? i tried some but still not working .. i tried below format "url-filter": "//\\\\.googlesyndication.com.*" "url-filter": "\.googlesyndication.com.*" "url-filter": "//////.safeframe.googlesyndication.com.*" – Hitarth Sep 24 '21 at 13:51
4

Assuming you don't mind ads being served to iOS 10 or earlier, then iOS 11 and later include content blocking rules.

Overview

Suppose I have a UIViewController subclass which has a webView property pointing to my WKWebView instance. I implement a custom method called goHome which visits some defined "home" URL. Before I do this, though, I might want to load content blocking rules into my web view. I get things started with something like this:

- ( void ) viewDidLoad
{
    [ super viewDidLoad ];

    // (other setup stuff here...)

    [ self reloadContentBlockingRulesAndGoHome ];
}

The 'reload blocking rules' stuff loads rules if I'm on iOS 11 or later. If you need support for other versions of iOS, see other answers. Forgive the white space layout oddities here, I have a quirky coding style when it comes to Objective C messages that include blocks in the parameters:

- ( void ) reloadContentBlockingRulesAndGoHome
{
    if ( @available( iOS 11, * ) )
    {
        [
            WKContentRuleListStore.defaultStore
                compileContentRuleListForIdentifier: @"ContentBlockingRules"
                             encodedContentRuleList: @"...see below!..."
                                  completionHandler: ^ ( WKContentRuleList * ruleList, NSError * error )
            {
                [ super viewDidLoad ];

                if ( error == nil )
                {
                    [ self.webView.configuration.userContentController addContentRuleList: ruleList ];
                    [ self goHome ];
                }
            }
        ];
    }
    else
    {
        [ self goHome ];
    }
}

That encodedContentRuleList parameter takes a JSON string. I tend to put that in its own method for clarity, so the code above might actually say:

[ WKContentRuleListStore.defaultStore
     compileContentRuleListForIdentifier: @"ContentBlockingRules"
                  encodedContentRuleList: [ self contentBlockingRules ]
                       completionHandler: ^ ( WKContentRuleList * ruleList, NSError * error ) { ... } ];

So what would [ self contentBlockingRules ] do? That just returns an NSString giving the content blocking data. Without integrating with some kind of third party ad block service, all you can do is inspect individual pages of interest and start noticing common class names or IDs of HTML elements that contain adverts, plus notice (from e.g. the Safari Web inspector's "Network" tab) all the domains from which advertising material is pulled. It's an illuminating, if somewhat depressing process, once you realise just how much advertising material is included on a typical web site now.

Rules JSON example

Read this carefully:

Here's an example that includes some CSS selector blocking stuff, blocks a particular URL from the server itself (e.g. some custom ad code they might be serving themselves), plus some rules for generalised third party domains you want to block (I use these a lot).

- ( NSString * ) contentBlockingRules
{
    return @" \
    [ \
      { \
        \"trigger\": { \
          \"url-filter\": \".*\" \
        }, \
        \"action\": { \
          \"type\": \"css-display-none\", \
          \"selector\": \".some_css_class, .some_other_class, #some_css_id\" \
        } \
      }, \
      { \
        \"trigger\": { \
          \"url-filter\": \"\\/some\\/very-specific-filename\\\\.js\", \
          \"resource-type\": [ \"script\" ] \
        }, \
        \"url-filter-is-case-sensitive\": true, \
        \"action\": { \
          \"type\": \"block\" \
        } \
      }, \
      { \
        \"trigger\": { \
          \"url-filter\": \".*\", \
          \"if-domain\": [ \"doubleclick.net\", \"adservice.google.com\", \"etc\" ], \
          \"resource-type\": [ \"script\" ] \
        }, \
        \"action\": { \
          \"type\": \"block\" \
        } \
      } \
    ]";
}

It's nasty taking JSON and encoding it into an NSString - as you can see, the result is messy with an awful lot of escaping going on, especially for some of the URL parts which are escaped once for JSON, then escaped again for the NSString literal. There are probably cleaner ways to do this that make maintenance of the JSON data much easier.

Final thoughts

Due to the sheer volume of advertising and tracking code on many sites, the difference in page load speed when you block stuff can be dramatic. It's worth the effort to give your users a better experience and hit their mobile data less, BUT:

  • You might be breaching terms and conditions of service on sites you visit
  • If the sites update their code then you might end up missing some ads you intended to block
  • There's a somewhat worse risk that a site update or too-general blocking rule may accidentally hide "legitimate" non-advert page content.

Rather than just copy and paste anything above, please instead carefully read the Apple Developer Documentation on the various techniques and framework methods above, so that you understand limitations, error handling requirements and so-forth. The above examples are just to get you started on the concepts, but it's important to write real code from a position of fully understanding the things you are doing - your app users deserve a stable experience :-)

Andrew Hodgkinson
  • 4,379
  • 3
  • 33
  • 43
  • 1
    You could create a Dictionary with key/values, then serialise it to pass it as a String. Or you could also use Swift raw strings """ json content """, so you can avoid escaping the json manually. – vicegax Oct 09 '20 at 11:26