Show Menu

Need an iOS Developer?

Submit your 30 day Job Listing for FREE

In this tutorial I am going to show you how you can create a Safari Content Blocker Extension using Swift.

Safari Content Blocker

So, What exactly is a Safari Content Blocker? Well, It is an extension that you can create rules which will be applied to Safari on the loaded device. I think Apple sums it up rather nicely:

Safari content-blocking rules can hide elements, block loads, and strips cookies from requests. Content-blocking rules are created in a structured format ahead-of-time, declaratively, rather than running extension-provided code at the moment a decision about blocking needs to be made. WebKit compiles the ruleset into a bytecode format that it can process efficiently at runtme, reducing latency between when a page request is created and when it is dispatched over the network. Safari does not request undesired content. By avoiding unnecessary or unwanted downloads, Safari uses less memory and has better performance.

For those who are having a little trouble with Apples famous Jargon style writing let me summarise as succinctly as possible. When you create an app, you can add the Safari Content Blocker Extension into it. In the Content Blocker you will set out/define rules. For example, you can stop images from downloading, Cookies from storing and JS from running on a specific URL. Only content that passes the validation of your filters will display.

Create Project

The first thing you need to do is create a new Simple View Application. You can call it whatever you like and save it wherever you find most useful.

Create Content Blocker Extension

Now that you have your basic application created we need to add a new application extension. We do this by going to File > New > Target. This will open up a Dialogue window for us and from that window, in the left hand side pane select Application Extension. Now, from the right main pane, select Content Blocker Extension:

Swift Add Content Blocker Extension

Click Next. You will then be asked to name your content blocker. I have called this one SuperEvil

You will then get a message asking you to Activate “SuperEvil” Scheme. Click Activate:

activate content blocker theme

Once that is done, go to your Project Navigation Pane. You should see a new folder called SuperEvil and a subfile of that folder called: blockerList.json. Double click that file. by default the code inside should be:


[
    {
        "action": {
            "type": "block"
        },
        "trigger": {
            "url-filter": "webkit.org/images/icon-gold.png"
        }
    }
]

Block phrases or urls with Safari Content Block

Let me show you a very quick example. Let us assume that you are fed up of reddit. (For the record, I love reddit) You no longer want to display any pages from the reddit website. This includes subdomains. Then you would replace the above code with:


[
    {
        "action": {
            "type": "block"
        },
        "trigger": {
        "url-filter": ".*reddit.*"
        }
    }
]

In the above example, we’re using the regular expression .* to mean “any text”, so our trigger will match “a URL that has any text, then reddit, then any other text.”

Try it. Build an Run your app, then press CMD + SHIFT + H to return to the home screen of the simulator. Select Safari and go to reddit.com.

What? It still Loads?

Well, that is because the user has to physically enable the content blocker. Why? Well it’s for security and because apple like to make sure the users know about everything before they do it. This is a good thing. Let’s say that you are Google and your competitors are Yahoo, Bing and DuckDuckGo. How easily would it be for Google to just simply block users from going to those websites?

If you are found to be abusing this ability, by blocking full URLs from your competitors without the users knowledge your app will be removed and you will be deleted from the Apple Developer Program. Make sure that you are specific with everything that your app is doing.

So to get this to work, you need to run your app, use CMD + SHIFT + H to go to your simulator home screen, Select Settings, then select Safari and scroll down until you see Content Blockers:

Safari Settings Content Blocker

Now you will be taken to the screen that lists all the Safari Content Blocker Extensions that are installed.

Safari Content Blockers Turn-On

As you can see, the Content Blocker is not called SuperEvil like we specified earlier. When we named our blocker SuperEvil earlier, we were only doing it for ourselves. For the iOS Simulator, or an iOS Device, it will take the name of the app.

Click the Switch to turn it on. Now, Return to safari and try to go to http://reddit.com. YAY, it’s blocked.

However, there is now a slight problem. This blocker will stop any url with the phrase reddit. Try it, go to the Google website and search for reddit. That page no longer loads either. This would be fine if you wanted to block bad words for a childrens app but might be annoying if you accidentally blocked lots of commonly used words and made it impossible for the user to do anything. To get around that, we would change the “url-filter” line to this:


"url-filter": "https?://(www.)?reddit.*"

What we are saying here is, get the url scheme https? which, with the ? can either be http or https. Check to see if there is a subdomain or not: (www.)?. Look for the exact word reddit and then check for any text after that: .* which can be, for example .org, .co.uk, .com etc…

These operators form a regular expression. Here is a list of regular expressions that you can use with a Safari Content Blocker extension:

Syntax Description
.* The search matches all strings with a dot appearing zero or more times. Use this syntax to match every URL.
? The search matches the preceding character zero or one time.
* The search matches the preceding character zero or more times.
. The search matches any character.
+ The search matches the preceding term one or more times.
\. The search explicitly matches the dot character.
(abc) The search matches groups of the specified characters.
[a-b] The search matches a range of alphabetic characters.

At the moment, In order to get any changes to take effect you will need to go back to the settings and turn the Safari Contact Blocker extension off and then back on again. We don’t want this.

Auto Update Safari Contact Blocker

We are now going to make our new rules update every time we open our app.

Go to your AppDelegate.Swift file.

First, we need to make sure that the app knows it is supposed to use methods and classes for Safari, Import the Safari Services framework:


import SafariServices

Find the function applicationDidBecomeActive. This is where we will add our code.

Then, we need to define our id, which is our Bundle identifier.


let id = "com.ios-blog.SafariContentBlocker"

Now we are going to use the method: reloadContentBlockerWithIdentifier(id) from the SFContentBlockerManager Class (Link to Apple) :


SFContentBlockerManager.reloadContentBlockerWithIdentifier(id) {error in
    guard error == nil else {
        print("Failed to reload")
        return
    }

    print("Reloaded")
}

If you are wondering what guard is. Check out our intro to the Swift Guard syntax.

Run your app, now you should get “Reloaded” in the console.

If for any reason you get “Failed to reload”. Change the code above to:


SFContentBlockerManager.reloadContentBlockerWithIdentifier(id) { (result) -> Void in
    print(result)
}

This will give you a better answer as to what is going wrong. For example, if you have an error with the JSON that was entered, either with extra characters or the wrong format you will see an error like:

Optional(Error Domain=ContentExtensionsDomain Code=1 “(null)” UserInfo={NSHelpAnchor=Extension compilation failed: Failed to parse the JSON String.})

You will also get this error if you add comments in the file. So best to leave them out.

Block CSS with a Safari Content Blocker Extension

With this, you can search ann entire page and select certain elements. Let’s say that you didn’t want any link, on any website regardless of what that links says to click through to the reddit website. We can target those specific links like so:


[
    {
        "action": {
            "type": "css-display-none",
            "selector": "a[href*='reddit']"
        },
        "trigger": {
            "url-filter": ".*"
        }
    }
]

Give it a try, head over to Google in the simulator and search for reddit. As you can see all the titles and clickable links for reddit have gone.

Now, let’s say that you want to allow people to go to Reddit.com but you want to stop showing all images:


[
    {
        "action": {
            "type": "block"
        },
        "trigger": {
            "url-filter": ".*",
            "resource-type" : ["image"],
            "if-domain" : ["reddit.com"]
        }
    }
]

There you have it. Go forth and create some devilish devices.

Summary

As part of our Swift Tutorials collection, this tutorial has shown you How to create a Safari Content Blocker Extension which can block a specific url from loading. We have looked into selectors so that we can block certain elements from the page loading (known as Creating Safari Content-Blocking Rules) and have also been introduced to the reloadContentBlockerWithIdentifier method which allows us to automatically update the blocker when the app is launched.

having issues?

We have a Questions and Answer section where you can ask your iOS Development questions to thousands of iOS Developers.

Ask Question

FREE Download!

Get your FREE Swift 2 Cheat Sheet and quick reference guide PDF download when you sign up to SwiftMonthly


Sharing is caring

If you enjoyed this tutorial, please help us and others by sharing using one of the social media buttons below.


iOS-Blog Admin Team

Written by:

We're here to help.

Comments

There is currently only One Response to “How to Create a Safari Content Blocker Extension in Swift”. Leave your comment!

Thanks for sharing, this was really easy to follow tutorial but I’ve had one problem. I’ve got this error print out( Optional(Error Domain=ContentBlockerErrorDomain Code=1 “(null)”) ) so I tried to change the id to: “com.ios-blog.SafariContentBlocker.SuperEvil” and it worked.


Leave a Reply

Would you like to sign up to the mailing list by our sister site: SwiftMonthly?