It is now time to review what we learned in Project 4 (Easy Browser). Personally this was a rather big step up in difficulty from previous projects and the speed with which Paul goes makes me feel like I am a bit walking on eggs. The new subjects were clearly explained but if I didn’t follow the text alongside the videos I confess I would be completely lost. Sure, English is not my native language but I wonder why the 99% of the people who work in programming speak so fast, it seems like they have to. I wonder if the deadlines of the industry make them feel like they have to rush but, for sure, this is not how I want to end. I will take my time and possibly lag behind but I will understand what has been explained to me! That is my goal!
Here is a summary of the techniques we learned in this project—beside consolidating previous ones—.
loadView()
: this is a method used to load the page in the WebKit framework.WKWebView
: this is the kind of view used by said framework.- delegation: I feel this is the beast of the chapter, making someone do something on your behalf! Sounds already scary in real-life, imagine when talking to a computer.
URL
: everyone knows what a URL is but maybe few of us have ever used it outside of a web browser (I mean, the one that your computer comes equipped with).URLRequest
: this is part of theload
method of aWKView
instance.UIToolbar
: this is what the name says, a UserInterfaceToolbar where tools get positioned—hopefully ready to do something.UIProgressView
: this was a nice addition, sadly I have never seen this inside of a shipping app but the next part, KVO, takes the crows of the maddest of the new features learned.- KVO (or key-value observing) allows us to monitor the changing of some value in our code.
Review
- All view controllers have a
toolbarItems
property, just it is our job to show it or not! - We can conform to as many protocols as you want, just one thing is important to remember: if we are attaching a protocol to a
class
, the main parent of our subclass must come first, whereas if we are usingstructs
, the order doesn’t matter.- The other option in this question was: “All actions in an alert controller need to have handlers attached”. This made me think a bit, but then I remembered we used the “Cancel” action which didn’t have one.
- Flexible spaces automatically take up all available remaining space.
- Conforming to a protocol means adding the properties and methods required by that protocol.
- Here the other option was a huge trap: “URLs are strings in Swift”. In theory, if we hardcode them, they are, but just because we need to write text. In reality they are much more complex than an array of characters!
- The next question was my first mistake in all of these reviews. Well, it had to come sooner or later, right? The options were:
- We can use
#selector
to point to a specific method in a different object. - Escaping closures can continue running after your program has finished.
- I chose the second one because this is how I had understood it and because I thought that
#selector
simply told the handler which method to call. I then went back to the explanation for escaping closures which was:
- I chose the second one because this is how I had understood it and because I thought that
- We can use
the closure has the potential to escape the current method, and be used at a later date. We won’t be using it that way, but it has the potential and that’s what matters.
So I clearly confused that “later date” for “your program has finished”. Nice…
- Refactoring is about improving our code without changing its behaviour.
- Calling
sizeToFit()
on a view makes it take up the correct amount of space for its content. - A web view’s navigation delegate can control which pages should be loaded.
- I cannot say I understood this: we just set our
ViewController
class to be the webView’s delegate, nothing else. How does that directly control which pages should be loaded? From what I see in the code it seems that as our class is aWKNavigationDelegate
it can call some methods that influence that? Who knows?
- I cannot say I understood this: we just set our
- You can optionally provide a context value for your key-value observers.
- I answered this one because I was sure the other one was wrong but I cannot say I understood why this one was the correct one. In our code we add an observer but what is this? Option-clicking on the
addObserver
method shows its description and says that thecontext
parameter is of typeUnsafeMutableRawPointer?
. Ehm… what? Maybe we just need to know that the question mark is all what matters: it is an optional! Clicking on this type opens the Xcode documentation under Swift > Swift Standard Library > Manual Memory Management … and it is very long and hard to understand… Maybe it should just sleep there safe and sound!
- I answered this one because I was sure the other one was wrong but I cannot say I understood why this one was the correct one. In our code we add an observer but what is this? Option-clicking on the
- Progress views show a colored bar indicating how much of a task is complete.
- Delegation allows one object to respond on behalf of another.
loadView()
is called beforeviewDidLoad()
.
Challenges
Challenge 1: If users try to visit a URL that isn’t allowed, show an alert saying it’s blocked.
This looks like a piece of cake of a challenge but, in the end, it is not.
I thought I had understood that the place where we check for the website having a host and, therefore, being allowed, was in the webView(_:decidePolicyFor:decisionHandler:)
method. I read it like this:
If the url we are using has a host, run the .allow decisionHandler for every website in the websites array and return. If we are still here after that, just call the .cancel decisionHandler, that is do not let us load that website.
I therefore wrote an else
statement containing an alert controller after the brace ending the if let
statement. This doesn’t do anything at all apart from preventing me from navigating to that website.
I managed to get a version running what it should but I am not sure this is completely correct. As we do not get to after the if let
statement if we do not press anything wrong I put the alert controller after it and it somehow worked, showing me the alert every time I tried to navigate away and forbidding me to load that page. Just, once, it blocked a website that had the good domain… bah…
If I try to create an else
statement after the if let
one I get this error:
Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Completion handler passed to -[Easy_Browser.ViewController webView:decidePolicyForNavigationAction:decisionHandler:] was not called’
while if I write it after the if host.contains(website)
one I get this error:
Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Completion handler passed to -[Easy_Browser.ViewController webView:decidePolicyForNavigationAction:decisionHandler:] was called more than once’
By now the app is doing what it should but I guess I will have to come back to here to see what can be done to improve. If you read this and find something that I should do better, please let me know. Here is the new webView
method call:

Challenge 2: Try making two new toolbar items with the titles Back and Forward. You should make them use webView.goBack
and webView.goForward
.
Generally speaking, I have to remind myself that when something looks easy I am just plain wrong!
I created a new UIBarButtonItem
with a title of “Back”, a style of .plain
, a target of self
and an action of #selector(webView.goBack)
. I got this error:
unrecognized selector sent to instance 0x7fbd6120b710
Luckily enough the fix was as easy as to take a hammer and knock a few times on my head! Had I already forgotten that the ViewController.swift class is just a delegate of webView? Changing the target to webView
solved the thing.
While solving the forward button I decided to set the title of both buttons to some arrow emoji, to spice up the thing a bit!
Nice! Done!
Challenge 3: For more of a challenge, try changing the initial view controller to a table view like in project 1, where users can choose their website from a list rather than just having the first in the array loaded up front.
I guess what should be done here is to make a copy of the project so that we do not risk messing things up. I would really like it if it would be easier to change the name of the Xcode project so that, for example, in this case, Easy Browser becomes Easy Browser Tabled as I want to add a Table View, but, well, there are just too many things to learn, right? Also, making a duplicate while the other project is under version control with Git makes for a very unpleasant experience.
I am now undecided if we need an extra view controller for the table view or not. I guess we do? Just because if I just use what we have the webView will be destroyed, right?
In Main.storyboard lets try to erase the current Navigation controller, create a new TableViewController and embed that one in a Navigation controller. We then create a new CocoaTouchClass file, call it TableViewController.swift and assign it to our new view controller in the storyboard.
In TableViewController.swift;
- copy, comment out and past in the
websites
array. - in the
numberOfRowsInSection
method complete withwebsites.count
. - erase all unnecessary code and, in this case, the
numberOfSections
method. - uncomment and complete the
cellForRowAt
method.
In ViewController.swift:
- create a global variable:
var websiteToLoad: String?
- change, around line 48 for me:
let url = URL(string: "https://" + websiteToLoad!)!
- erase the
openTapped
method and the relative bar button items as they are no longer needed. - In the webView method erase the loop and move its code outside so that it reads:
if host.contains(websiteToLoad!)
…
In the Storyboard, don’t forget to set the new Navigation Controller as “Is Initial View Controller”.
Back in TableViewController.swift, write the didSelectRowAt
method as follows:

Now … this is the moment of truth, at this point I do not know if things will work. I cleaned everything, there is no error and no warning. Let’s build and run! Cross your fingers with mine!
OH MY GOODNESS IT WORKS!!!
Just a little thing: a title to the table view!
I mean, this is fantastic, I never thought I could do this! Click here to go to the GitHub link for this project in its latest incarnation or here if you want only the first two challenges completed.
Thank you Paul for making us believe in ourselves!
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!