Hacking with Swift – Challenge 4

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 the load method of a WKView 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 using structs, 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:

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 a WKNavigationDelegate it can call some methods that influence that? Who knows?
  • 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 the context parameter is of type UnsafeMutableRawPointer?. 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!
  • 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 before viewDidLoad().

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:

Screen Shot 2019-02-26 at 18.32.28

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 with websites.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:

Screen Shot 2019-02-26 at 19.35.13

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!

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: