Bob Lee Course - Consolidation of first 8 lessons!

So it has been several days but that cannot be helped sometimes! The good thing is that I'm in no rush to 'finish' this course. Nor should I be! It's much better to let the knowledge sink in and learn about it at deeper level rather than learning more and more new concepts, which I can't make complete sense of.

Something I've been thinking of for a while is - as a teacher - levels of understanding within learning. Something at my school in Dubai is 'Do It, Use It, Own It'. This is essentially a way of setting different challenges, from most basic (Do It) to the deepest level (Own It). Let's say that for a Maths lesson, I am teaching Roman Numerals. I would have the 'Do It' task as recalling numerals up to 100 - something that recalling is basically required for. The 'Use It' level would be to explain why e.g. 49 is XLIX rather than IL. This is obviously more complex. An 'Own It' question would be at a deeper level e.g. look at this new number system - what are the rules? How is the system different to Roman Numerals? Can you evaluate which is most effective? How does this compare to the number system we use today?

I've used this means of comparison in order to evaluate where I currently am. For a while, I was very much at the Do It stage. I was learning various concepts but my understanding was limited. When faced with any problem solving challenge, I would flounder and struggle to apply my knowledge. I feel like I've overcome that with the various courses I've utilised in recent months; it's been approximately 4 months of consistent coding: the longest stretch I've had since my initial run from April to August, 2015. Now I can explain and give examples for different Swift features and explain why a way around a certain problem is better than another. I can recall most of the syntax and through practice can apply a lot of it.

Yet I am not at, or that near to the 'Own It' stage. At that point, I would be doing certain aspects automatically, and be able to confidently apply my knowledge. That is where I want to get to!


And here begins what I want to do for this entry - look over Bob's first 8 lessons and consolidate what I want to know. This may require some extra reading and examples but I want to be able to understand at a deeper level what he has explained so far in his course. Here we go!

Start Time - 11:08

Let's start with a summation of what Bob has actually explained in his Intermediate Course:

1. Optionals - implicit and explicit types first; implicit unwrapping (optional binding)
2. Optionals - classes with optionals, force unwrapping, guard statement, defer statement
3. Error Handling - do/try code, func with throws,
4. Error Handling - typecasting, upcast/downcast, casting with arrays,
5. Generics - code 'smell', use of < >, 'any', mutating func, extensions

So a lot there! I'm going to pause before diving in

Paused at 11:17 (9 minutes so far)

Continued next day at 11:57

Yes so I had to leave that entry there - just having laid out a summary of all of the areas from Bob's course so far. Let's start with the first bit - optionals:

1. Optionals

Explicit/implicit declaration - this is simple. You can make a value explicit by stating its type e.g. let name: String = "Josh". If this were implicit then it would simply be let name = "Josh" - Xcode would infer that it is of type string. Same with other types. OK, so far, so easy!

Optionals come into this now. So they were created in order for programs to STOP crashing! This makes sense as it gives them more purpose; I never really got that before. Anyway, optionals are declared with the ? e.g. var name: String? = "Josh". Another key point that this now of type STRING OPTIONAL, rather than just String. It needs unwrapping to get it as String....

*Another interesting point with optional Ints is that they cannot be added or interact with each other at all. Again, they need to be unwrapped.

Optional binding - this is where if/let is used to store a value in a temporary constant E.g

let song: String? = "Under the Bridge"

if let songValue = song {
print(songValue)
} else {
print("There is no value")
}

In that case, "Under the Bridge" would be printed; songValue is where the unwrapped value is now stored. Also, if the value were nil (nothing), then it would still be safely unwrapped this way. Using ! would be forced unwrapping, but this must only be used when you know 100% that there is a value and NOT nil. Force unwrapping when there is no value means the program crashes!

Cool, all that seems clear. Next!

2. Optionals

Classes - yes optionals can be used in classes. They can be used for custom types- handy to store nil. The ? gets automatically put in when accessing a property that is an optional.

Implicit unwrapping - this is the same as optional binding (like with my Under the Bridge example)

Guard statement - these promote emptiness/zen! I like Bob's analogy here of wanting things zen/empty. Basically, it goes straight to seeing if there is no value, then checks if there is one. Having just read up a bit more on this, I can only see guard statements used within functions.

let myName: String? = nil

func stringCheck() {
guard let name = myName else {
    print("There is no value")
return
    }
    print(name)
}


stringCheck()

Simple enough and actually the print(name) bit is not strictly needed - it will work still as nothing is printed if there is a name; the first bit is the print line for if there is no value. 

Multiple values - yes this is a good point as the code can get very complex if you use a separate optional binding line for every value. Instead you can combine these - 

var driverName: String? = "Fernando Alonso"
var driverCar: String? = "Renault"
var driverWins: Int? = 32

func unwrap() {
    if let name = driverNamelet car = driverCarlet wins = driverWins {
        print("Driver is \(name), who drives for \(car) and has won \(wins) races")
   } else {
        print("Error")
}

The guard let can be used too. 

func guardUnwrap() {
    guard let name = driverNamelet car = driverCarlet wins = driverWins else {
        print("something is missing")
        return
    }
}

Again, I could add in print("Drive is...) etc.   print("Driver is \(name), who drives for \(car) and has won \(wins) races")


Defer - not sure about this so will look up! Having read up using https://medium.com/@rwgrier/swift-defer-statement-e16526b34f93
I'm still not too clear! What I can glean from this is the defer statement is not used too often. It is executed in reverse - so it will go to anything else in the block, then to the defer line. It is a way of exiting the code.

3. Error Handling

Again, this is something I saw little point in when learning about the on the Treehouse course. Bob has made the purpose clearer! Basically with Error Handling, it is anticipating problems and creating specific, customised code for said errors. Enums are used to make the custom error messages. Also can be called 'design error' - designing code to be able to deal with potential errors.

func checkHeightError(height: Int) {
    
    if height > 200 {
        throw ErrorMessage.maxHeight
    } else if height < 140 {
        throw ErrorMessage.minHeight
    } else {
        print("All is good!")
    }

}

So before hand, the enum with the error messages would have to be created.

Error Testing - this is SPECIFICALLY to test for errors:

do {
    try checkHeightError(height: 20)
catch ErrorMessage.minHeight {
    print("You are too short!")
catch ErrorMessage.maxHeight {
    print("You are too tall!")
}


So within classes too: 

class Course {
    var name: String
    init(name: String) {
        if name == "" {
            throw NameError.noName
        } else {
            self.name = name
            print("You've created an object of Course!")
        }
    }
}

Another do/catch block. 

do {
    let newCourse = try Course(name: "")
catch NameError.noName {
    print("You need to enter your name!")
}

This is very simple, elegant code:

let newCourse = tryCourse(name: "")


Let's just read up a little more to clarify some stuff. I get Error Handling in terms of the purpose now but need to brush up on the syntax - there is a LOT in the above.

Reading up on the official documentation, https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html, there are FOUR WAYS to handle errors:


1. Propagating errors using throwing functions

  1. func canThrowErrors() throws -> String
  2. func cannotThrowErrors() -> String

So for a function to use error handling, you need the throws keyword basically!

This is all within a vending machine class:

  1. func vend(itemNamed name: String) throws {
  2. guard let item = inventory[name] else {
  3. throw VendingMachineError.invalidSelection
  4. }
  5. guard item.count > 0 else {
  6. throw VendingMachineError.outOfStock
  7. }
  8. guard item.price <= coinsDeposited else {
  9. throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
  10. }
And of course we would need to have enum with the different error messages! Guard statements are used here. 

2. Do/Catch - so this is the second way. Writing out the do/catch block (like the one I've put in a bit above) is another method basically of doing error handing. 

3. Converting to optionals - this was the try? one. Not sure when to do that one.

4. Defers statements - the 'clean up' way. 

OK, that gives me more understanding, but it's clear that I'm still getting used to the syntax for Error Handling - in some ways still at the 'DO It' stage for this!

4. Type Casting

Right. This was when you specified something to change type using the 'as' keyword. 

let newBob = bob as Human

This was after creating a Human and then Korean class (Korean being  subclass). In the above, newBob is now of Human type and has access to just the Human functionality. 

var name = "Josh" as Any

var number = 33 as Any


So these have been upcasted to Any, which is of course the parent of all the main types. Going from Any to String/Int would now be DOWN casting. 

Putting into an array - var anArray = [namenumber]

Then using optional (better than the forced with ! way)....

let newerNumber = anArray[1asInt

This is safer IN CASE an error is made e.g. specifying that newNumber will be an Int but is actually a String (it isn't but could be!)

The upcasting is clever for different types. If you have a mixture of subclasses with the parent class, it will cast them all to the shared type in common - the parent one. E.g. if I have a mixture of custom types (UK/Korean) for some names, I could then upcast them all to the parent class of Human....

let bob = Korean()
let josh = British()
let cath = British()
let jenny = Korean()

let humans = [bobjoshcathjenny]

As arrays HAVE to be of the same type, it means that all of the values in humans are now of Human type! Neat!

For-in loops can be used as well to access the functionality etc.

for human in humans {
    if let korean = human asKorean {
        korean.singGangnamStyle()
    } else if let british = human asBritish {
        british.sayHello()
    }
}


5. Generics

Let me see if I can remember this before checking....Ah yes, the was about using < > to make values of different types being of any type. Better than creating separate functions for each type/each time. 

Code 'smell' - knowing that something is potentially up with the code. 

Functions for generics

func printElement<T>(array: [T]) {
    for element in array {
        print(element)
    }
}

So the above uses 'T' - this stands for 'Type' - in this case, any type can be accessed. So for actually using this function, it means that an array of ANY type can be used!

Mutating - 

struct Family {
    
    var members: [String] = []
    
    mutating func push(member: String) {
        members.append(member)
    }
}

struct Family<T> {
    
    var members: [T] = []
    
    mutating func push(member: T) {
        members.append(member)
    }
}

The second one is more GENERIC as it could have names which are actually numbers (e.g. driver numbers, football players etc.).

The mutating keyword is used because the value of members is going to CHANGE, so mutating has to be used. 

Extensions - I've seen these before in a (Nick Walter's?) course. It's basically adding more options for using a certain type. It can be used for custom types too.

extension Family {
    
    var firstElement: T? {
    
    if members.isEmpty {
    return nil
    } else {
    return members[0]
        }
    }
}

There it is - using some implicit unwrapping there!

Stopped at 13:19 (1 hour 22 minutes today; 1 hour 31 total)

Well, that was well worth it. I definitely have a deeper understanding of optionals, error handling, type casting and generics. They're all tricky, advanced concepts but by making sense of all of these, I definitely know them a bit better now. 

*From now on, I will do these consolidation blocks after a MAXIMUM of five entries. Like I've said before, there is NO RUSH to crack through the courses. It makes so much more sense to pause and ensure that I know the content better. I will do another Bob entry later or tomorrow!

Comments

Popular posts from this blog

*Xcode Project Entry 2* F1 Quiz - part 1

Angela Yu Course Part 10 (up to lesson 112)

Angela Yu Xcode 12 Course - Part 7 (lectures 74 to 79)