Today’s goal is to create a basic browser.
To get started, we choose a Single View app template, we name is Easy Browser (or Project4, as you prefer), save it to a sensible location and hit Create. We then switch to Main.storyboard and, with our ViewController selected, we go on to Editor > Embed In > Navigation Controller.
Creating a basic browser
This changed in iOS 12 as the previous framework for creating web content was deprecated; we now have WebKit, which is an amazingly powerful framework!
We import it via the import
statement at the top of our ViewController class and then set up a property where to store the WebKit View.
Before the viewDidLoad()
method we add a call to one of WebKit’s own methods, loadView
. This introduces the concepts of delegation, where we import functionalities from a protocol and promise to use them in our own class.
We instantiate a WKWebView
, assign its navigationDelegate
to self
(i.e.: the view controller where we are now) and then we assign it (the webView) to the view
property of our view controller (that is, the whole view!).
Then, inside viewDidLoad()
, after the super
call, we store a web URL
containing a web address, we attach that url (shorthand for uniform resource locator) to a call towebView.load(URLRequest(url: ))
and then we allow the swiping back and forward of the pages in the browser with the call to the allowsBackForwardNavigationGestures
property.
For some reason, not all the websites I tried were successfully built in the Simulator.
Choosing a website
We now want to give the user a chance to choose among a few websites and to this we are going to use the .actionSheet
style for the alert.
Inside viewDidLoad
we add navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Open", style: .plain, target: self, action #selector(openTapped))
, bearing with Xcode’s complaints for a bit more if we can.
We then write the openTapped
function which looks something like this:
Each addAction
call allows us to have an extra button on the action sheet.
We then write the openPage
method, which is just there to set the URL to https://
plus one of our action titles and to load the webView
.
Finally, we call the webView(didFinish:)
method and set the title of our view controller to the webView’s title when the loading of the page has finished.
Monitoring page loads
We are going to meet some important friends here, first of all the UIToolbar
that sits invisible at the bottom of all our view controllers until we call it! It contains an array of UIBarButtonItem
s for us to fill with what we want.
We will create a spacer
, which is a system button of type .flexibleSpace
that acts like a spring between all other buttons in the array to space them evenly out and a refresh button, which will call the webView.reload
method automatically.
We also create a progressButton
which is always a UIBarButtonItem
but this time with a customView
which we create as a UIProgressView
with a .default
style. We ask it to size itself to fit and then we populate our array.
From what I could understand, this array’s order reflects the left-to-right disposition of the elements.
Finally, we ask for the toolbar to be shown by setting its .isToolbarHidden
property to false.
Key-value-observers
Just adding a progress bar is not enough tough because nothing will fill it. We dive now into the realms of observers
and of keyPath
, a subject I feel will take its due time to sink in.
webView.addObserver(self, forKeyPath: #keuPath(WKWebView.estimatedProgress), options: .new, context: nil
Please refer to Paul’s article for the explanation on all what this does, I am pretty sure I could not use clearer words.
We finally call the observeValue
method that will manipulate the progress
property of the progressView
according to the webView
‘s estimatedProgress
.
Clear? Let’s say yes… Easy to reproduce for a novice like me? Nope… but such is life! One step at a time!
Refactoring
Our app has a fatal flaw in security, that is it lets the user navigate away to any link without checking if they should be allowed or not.
To solve this we need to refactor our code, that is rewriting it so that it is clearer and safer.
As a first step we add an array of strings to the top of our class to contain the allowed domains for our browser.

We then change our url
constant in viewDidLoad
to read URL(string: "https://" + websites[0])!
so that it reads from the first element of the array instead of hard-coding it.
The next step is to modify the openTapped
method with the addition of a loop so that our action sheet is automatically populated with how many websites we want to allow.
Finally, the hard bit: we need to decide if the navigation is allowed or not. This is done via a webView(_: decidePolicyFor: decisionHandler:)
method, which contains a new beast, an @escaping
closure (that means it can escape our code, that is it can be called now or later on). In summary, this function is to check if the website or the link clicked upon has or doesn’t have a host, a verified domain. And here is the code:

As always, here is the code for this project on GitHub and I would appreciate your opinion on my writing here.
Also, don’t forget to connect with me on Twitter and to say hello and thank you to Paul there as well!
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!