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]]];
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]]];
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
Assuming you don't mind ads being served to iOS 10 or earlier, then iOS 11 and later include content blocking rules.
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.
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.
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:
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 :-)