Hacking with Swift – Challenge 12

Hello everyone, this is my 52nd day of Swift learning and, even if a bit late still, I am going to tackle the second and third part of the program for Day 49 of the Hacking with Swift’s 100 Days of Swift initiative by Paul Hudson.

Yesterday we learned how to save data in two different ways, NSCoding and Codable and I guess today we will have to put all this acquired knowledge into practice. Let’s get started!

Wrap up

Here is a summary of what we learned in Project 12:

  1. It’s the job of NSKeyedArchiver to convert NSCoding objects into Data.
  2. Types that conform to NSCoding can be written to UserDefaults
  3. The Codable protocol can convert Swift types to and from JSON
  4. We shouldn’t save too much information in UserDefaults because it might slow down your app launch
  5. Nil coalescing lets us provide a default value to use if an optional is nil
  6. NSCoding uses string names for the keys it writes out
  7. Using Codable is preferred when you have an app that’s written only in Swift
  8. Adding NSCoding to a custom type means implementing a new initialiser and a new encode() method
  9. A type that conforms to NSCoding must be a class that inherits from NSObject
  10. Most built-in Swift types are compatible with NSCoding, including strings, integers, Booleans, and more…
  11. Our app’s default UserDefaults settings get loaded when the app launches
  12. UserDefaults lets us store program settings and user settings in one place

Challenges

Challenge 1: modify project 1 so that it remembers how many times each stop image was shown—you don’t need to show it anywhere, but you’re welcome to try modifying your original copy of project 1 to show the view count as a subtitle below each image name in the table view.

Fine… to say that I am awfully pissed would be a tremendous understatement. I am once more stuck on these challenges where we are just being told what to achieve, that it is within our grasp and that’s it.

I have been a very diligent student all my professional life, trying to understand every single corner of the subject before moving on, delving into books and other resources before considering myself even remotely satisfied.

But here…

I keep listening to people telling me “it’s a process”, “it takes time”… but all what I see now is that I am just losing my time.

I have never asked anyone for a given solution but if I have to learn I need to know where to study, not just try to hit my head against a wall hoping it will be the wall the one to yield.

This is just plain frustration!

Yesterday evening I sat here in front of my Mac for my daily hour and half of so of Swift and I just got stuck in front of the screen, not knowing what to do, not knowing where to look for a hint, a path, an idea… and yes, I browsed StackOverflow as well… no luck there…

When I was practicing cello 8 hours per day I always knew what I had to do or, if not, what I had to look for but here not… my teacher then was also saying that practice is everything and that quantity in the beginning is more than quality but he also stated that if one would practice badly for 10 hours a day he would not get a single inch closer to the goal. That was what the teacher was there for, to check my learning and to tell me NOT THE SOLUTION, but THE PATH!

I really need a path to follow for these challenges, even some maieutic questions to make me understand the solution that, as Plato said, is always already within me!


Whatever… after four hours (FOUR FUCKING HOURS!) of trying in front of the blank screen I decided to ask for help on the Slack channel and I received an hint that drove me on the right road. A single hint… do you understand?

I will now write here the solution I came to but I want this to be very loud and clear: I DID NOT SOLVE THIS CHALLENGE! I got an hint and without that I would have possibly never solved it! So, thank you to the guys who helped me and supported me but I am sorry, I cannot be positive right now: this way of learning (I mean the videos and all the rest) is great, awesome, just fantastic but these challenges are a plain mace hit in the face, are a torpedo against my self-esteem and believes, they are not what I need to learn something.

Some people will say: “Pfff… you little idiot Italian, your European school system has raised you with a teacher always telling you what to do”.

Sure, I take the accusation and I accept it but as I know this would come from some people raised with the US-school system, I would say: “Well… we have a much broader knowledge and general culture thanks to our system than yours”. But this is not the point, really! The point is that American-styled courses tell you to look on Google for a solution if you are stuck! I mean, really?! Instead of teaching you how to think!

I plainly refuse to go and Google (or Duck-duck-going in my case) a solution if I am stuck! I WANT TO LEARN! But for that I need a teacher who tells me HOW to do things, not only WHAT!


So, after learning that I needed a dictionary to store the times each pictures would have been viewed I created one like this var pictDict = [String: Int]().

Then, I modified the cell in the storyboard to become a “subtitle” cell and, therefore, modified the cellForRowAt method to include a phrase that would let the user know how many times a picture would have been seen.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
	let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)
	let picture = pictures[indexPath.row]
	cell.textLabel?.text = picture
	cell.detailTextLabel?.text = "Viewed \(pictDict[picture]!) times."
        
	return cell
}

I then added this pictDict[item] = 0 to the loadPictures method to set the viewed times to 0 for every single picture.

Then I created a save() method like this:

func save() {
	let jsonEncoder = JSONEncoder()
	if let savedData = try? jsonEncoder.encode(pictDict),
		let savedPictures = try? jsonEncoder.encode(pictures) {
		let defaults = UserDefaults.standard
		defaults.set(savedData, forKey: "pictDict")
		defaults.set(savedPictures, forKey: "pictures")
	} else {
		print("Failed to save data.")
	}
}

Inside didSelectRowAt I added this after the if let statement:

let picture = pictures[indexPath.row]
pictDict[picture]! += 1
save()
tableView.reloadData()

Finally, in viewDidLoad() I added code to load the data if saved data was found. I tried to write a load() method but as I had two keys I would have needed to add some parameters and more unpleasantries and I just decided to give up, given also the angry I already was yesterday evening…

Now the app works and saves this kind of data and I hope not to see this again anytime soon…

GitHub link: https://github.com/Cellomaster87/Storm-Viewer-/tree/challenge12-1a.

Challenge 2: modify project 2 so that it saves the player’s highest score, and shows a special message if their new score beat the previous high score.

My approach here would be the following:

  1. Create a new global property to hold the high score.
  2. Modify the final alert controller with a condition set to be triggered if score > highScore which, of course, would be true on the first run of the game.
  3. Save the highScore and load it on successive launches of the app.

Sounds like a plan, right? Now let’s see if I can smash it right in da face!


Point 1. was created without any issue and I also managed to make the final alert controller show me a message if I got a new score but, then, in the console, I get the “Failed to save high score” message…

Strangely enough when I load the app once more I do not get the warning “Failed to load high score” from the decode do-catch block… very interesting…and irritating of course…


I tried to move the call to save from the buttonTapped action to the startNewGame method so that

if score > highScore {
	save()
}

This time I didn’t get any error but when I launch the app again the score is not loaded…

Even stranger… I set print statements for both successfully loading the method and for not but nothing happens… it seems that the methods are never called and so even in viewDidLoad nothing gets to be called because there is no key “highScore” in user defaults. I must be that I am calling the save method from the wrong place…


Ok… fine… this may really be too much once more…

Everything was fine… all the logic, all the idea, all the planning, all the reasoning was perfect! Just… I didn’t have to encode Int into JSON as that was to be done only for custom types… yeah… nice…

I mean… in our projects we just saw the encoding and decoding of custom types and the methods used allow for using of Any objects so what? How could have I come to understand this without a handbook, a hint, a lesson on this?

Once more a colleague in learning—Rob Baldwin—made me notice this and…puff…the app started working.

I can only say that I am even more pissed than before because there is no real way we learners can understand why things are wrong and what can be done with it… Just desperation and frustration…

Anyway… here is the code.

Challenge 3: modify project 5 so that it saves the current word and all the player’s entries to UserDefaults, then loads them back when the app launches.

So, let’s try to make a plan once more and see if I can screw up everything once more and again.

  1. Create some property to store the current word and save it.
  2. Save the player’s entries
  3. Load them back if it is not a new game!

At first I thought this would have been harder because I thought a isNewGame Boolean would have been needed and, instead, I found that it works anyway right without it. Maybe it is full of bugs but it works! We will see…

So, I created an optional string variable called currentWord.

Inside the startGame method I have set it equal to title.

I then created a save method for both the currentWord string and the usedWords array. I called it inside the submit method just after the usedWords.insert(lowerAnswer, at: 0) line and inside startGame just before the reloadData line.

Inside viewDidLoad I set up the defaults as before, loaded the objects thanks to optional binding and, if this would have failed—actually, just at the first app’s launch—I would call startNewGame.

I got a bug at a certain point that didn’t save more than 2 words… but then, re-launching it, I could not reproduce it so I have asked a few colleagues to give it a look.

If you would like to see this please go here.

Conclusion

So… it’s been a difficult couple of days.

The Consolidation Day IV had been a big hit, but this was twice that and I am very scared in seeing Consolidation Day V coming up next… I don’t know if it will be another Waterloo or not but I have to confess I am not looking forward to it right now.

Thank you for keeping up with me today.

See you on 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

2 thoughts on “Hacking with Swift – Challenge 12

  1. Strangely, there aren’t so many comments here. So I’d like to say that your guides are being extremely helpful for me! As a complete beginner, it would’ve been almost impossible for me to solve this challenges without your help. Glad I found your website after searching for challenge solutions. Thank you a lot!

    Liked by 1 person

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: