Creating Safari Content Blockers - Extensions - iOS 9 Swift Programming Cookbook (2015)

iOS 9 Swift Programming Cookbook (2015)

Chapter 5. Extensions

Apple increased the number of extensions that we developers can write in the new iOS. One of the hot extensions that everybody seems to be talking about is the Safari Content Blocker, which allows a developer to specify which URLs or resources should get blocked in Safari tabs.

Extensions are separate binaries that sit inside your app’s bundle. They usually have their own naming convention and sit inside reserved folders inside your app bundle. It’s best not to mention what they are called on disk because Apple can change that at any time without us knowing. Because extensions sit in their own folders and have their own bundles, they do not share the same physical space as their container app. But, through some work, they can access the container app’s resources such as images and text.

5.1 Creating Safari Content Blockers

Problem

You want to create a content blocker that the user can add to their Safari browser for blocking specific web content.

Solution

Use the Safari content blocker extension.

Discussion

This is something I am very excited about. You can ignore the long list of content blockers popping up on App Store every day from now on.

This is how the Apple blocker works. When you create an app, you can add a Safari content blocker extension to it. In that extension, you define the rules for your content blocking, e.g., whether you want to block images, style sheets, etc. The user can then, after opening your app at least once, go into the settings on their device and enable your content blocker. From now on, if she visits a web page that your content blocker applies to, she will see only the content that passes your filters.

Let’s create a simple single view controller app and then add a new target to your app. From the iOS main section, choose Application Extension and then Content Blocker Extension (see Figure 5-1).

Figure 5-1. Adding a new content blocker extension to our existing app

NOTE

Give any name that you want to your extension. It doesn’t really matter so much for this exercise.

Now go to the new extension’s new file called blockerList.json and place the following content in it:

[

{

"action": {

"type": "block"

},

"trigger": {

"url-filter": ".*",

"resource-type" : ["image"],

"if-domain" : ["edition.cnn.com"]

}

}

]

Even though there is a specific type of formatting to this file, I think you can just read this as I’ve written it and understand what it is doing. It is blocking all images that are under the edition.cnn.com domain name. Now head to your app delegate and import the SafariServicesframework. Every time you change your content blocker, you will have to go to the settings application on the simulator and turn it off and on again so that the simulator understands that the extension is updated. We are now going to write a piece of code that automates that for us:

func applicationDidBecomeActive(application: UIApplication) {

//TODO: replace this with your own content blocker's identifier

let id = "se.pixolity.Creating-Safari-Content-Blockers.Image-Blocker"

SFContentBlockerManager.reloadContentBlockerWithIdentifier(id) {error in

guard error == nil else {

//an error happened, handle it

print("Failed to reload the blocker")

return

}

print("Reloaded the blocker")

}

}

Then reset your simulator and run your app. Send your app to the background, open Safari on the simulator, and type in “cnn.com”. This will redirect you to http://edition.cnn.com/ (at the time of writing this book). Safari will hit the filter we wrote and will discard all the images. The results will be lovely. Well, I don’t know whether a website without images is lovely or not, but it’s what we set out to do.

A user can always enable or disable a content blocker. To do that, you can go to the Settings app on your device and in the search field type in “blocker”. Then tap on the Content Blockers item that pops up (see Figure 5-2).

Figure 5-2. Searching for blocker will allow you to go directly to the Content Blockers settings section of Safari

Once there, you can enable or disable available Safari content blockers (see Figure 5-3).

Figure 5-3. The list of our Safari content blockers is shown here

Now that you have seen an example, let me bug you with some more details on that json file. That file contains an array of dictionaries with various configurations that you can enter. This book would grow very large if I wanted to describe everything there thoroughly, so I am going to explain the options for each field through some pseudo-json code if that’s OK:

[

{

"action": {

"type": "block" | "block-cookies" | "css-display-none",

"selector" : This is a CSS selector that the action will be applied to

},

"trigger": {

"url-filter": "this is a filter that will be applied on the WHOLE url",

"url-filter-is-case-sensitive" : same as url-filter but case sensitive,

"resource-type" : ["image" | "style-sheet" | "script" | "font" | etc],

"if-domain" : [an array of actual domain names to apply filter on],

"unless-domain" : [an array of domain names to exclude from filter],

"load-type" : "first-party" | "third-party"

}

}

]

Armed with this knowledge, let’s do some more experiments. Let’s now block all a tags in macrumors.com:

{

"action": {

"type": "css-display-none",

"selector" : "a"

},

"trigger": {

"url-filter": ".*",

"if-domain" : ["macrumors.com"]

}

}

NOTE

I have no affiliation with nor any hate towards macrumors.com. I find that website quite informative, actually. Check it out for yourself. I am using this website as an example only and I am not suggesting that content on that website is worthy of blocking.

Or how about removing the a tag on top of the macrumors.com page that is an id attribute equal to logo?

{

"action": {

"type": "css-display-none",

"selector" : "a[id='logo']"

},

"trigger": {

"url-filter": ".*",

"if-domain" : ["macrumors.com"]

}

}

Now let’s have a look at another example. Let’s start blocking all images on all websites except for reddit.com:

{

"action": {

"type": "block"

},

"trigger": {

"url-filter": ".*",

"resource-type" : ["image"],

"unless-domain" : ["reddit.com"]

}

}

Or how about blocking all elements of type a that have an href attribute on Apple’s web site?

{

"action": {

"type": "css-display-none",

"selector" : "a[href]"

},

"trigger": {

"url-filter": ".*",

"if-domain" : ["apple.com"]

}

}

See Also