Treehouse Intermediate Course Part 3 (Initialization)

Well here we go - next part of Pasan's course! Not going to waste time with a preamble but am honestly going to aim for 40 minutes max. It's 11:40 am (Dubai time) now so will finish at 12:20 - or as close to if I'm in the middle of something. 

Object Initialization

So what is init? Preparing the instance of a class/struct/enum by giving values to stored properties. Parameters are used - following the same rules as function parameters.
Default or optional values can be used to store properties. All stored properties must have initial values.
Structs have member-wise initializers.

Failable and Throwing Initializers

What about where init is not possible? It could depend on external data.

*All coding typed out by Pasan already - I'm just copying and pasting it. 

enum Day: Int {
    case sunday = 1
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
    case saturday

}

So in this example, Xcode knows that monday = 2, tuesday = 3 etc. Specifying the type (Int) means that the enum is initialized automatically. 

A quote from Pasan:

  • By marking an init method as failable, you allow the compiler to consider
1:57
the different paths of execution rather than handling it implicitly.

I'm not sure what this means so am going to go back a little.....

OK, the Int, means that there is an integer raw value for each case member. 

OK! Get this now! It is failable as you could put in 8 or anything larger - that would return nil, rather than halt the execution of the app! This is much, much safer and better! 

So to make init failable is to use the '?'

class Person {
    let name: String
    let age: Int
    
    init(dict: [String: AnyObject]) throws {
        guard let name = dict["name"] as? String, let age = dict["age"] as? Int else {
            throw PersonError.invalidData
        }
        
        self.name = name
        self.age = age
    }
}

Right, so am having a look at this and typing along as Pasan types. This code isn't quite the same as there was something added since. Also no throws in the original. 

This one has evolved with this enum included too:

enum PersonError: Error {
    case invalidData
}

And the ? is now not used...interesting....

So, I am now going to read up on failable initializers more, using the link that Pasan provided first of all...

*20 minutes gone, 20 to go!

It is sometimes useful to define a class, structure, or enumeration for which initialization can fail. This failure might be triggered by invalid initialization parameter values, the absence of a required external resource, or some other condition that prevents initialization from succeeding.
To cope with initialization conditions that can fail, define one or more failable initializers as part of a class, structure, or enumeration definition. You write a failable initializer by placing a question mark after the init keyword (init?).
Ok, that all makes sense so far....

The example below defines a structure called Animal, with a constant String property called species. The Animal structure also defines a failable initializer with a single parameter called species. This initializer checks if the species value passed to the initializer is an empty string. If an empty string is found, an initialization failure is triggered. Otherwise, the species property’s value is set, and initialization succeeds:
  1. struct Animal {
  2. let species: String
  3. init?(species: String) {
  4. if species.isEmpty { return nil }
  5. self.species = species
  6. }
  7. }
You can use this failable initializer to try to initialize a new Animal instance and to check if initialization succeeded:
  1. let someCreature = Animal(species: "Giraffe")
  2. // someCreature is of type Animal?, not Animal
  3. if let giraffe = someCreature {
  4. print("An animal was initialized with a species of \(giraffe.species)")
  5. }
  6. // Prints "An animal was initialized with a species of Giraffe"
If you pass an empty string value to the failable initializer’s species parameter, the initializer triggers an initialization failure:
  1. let anonymousCreature = Animal(species: "")
  2. // anonymousCreature is of type Animal?, not Animal
  3. if anonymousCreature == nil {
  4. print("The anonymous creature could not be initialized")
  5. }
  6. // Prints "The anonymous creature could not be initialized"
NOTE
Checking for an empty string value (such as "" rather than "Giraffe") is not the same as checking for nil to indicate the absence of an optional String value. In the example above, an empty string ("") is a valid, nonoptional String. However, it is not appropriate for an animal to have an empty string as the value of its species property. To model this restriction, the failable initializer triggers an initialization failure if an empty string is found.
Right, let's make sense of this all. In the 'Animal' example, any case where the empty string is used, then 'nil' is returned - this is a safe way of dealing with such cases! In the next two examples, it's about making the value equal to another, or if it is equal to nil, printing a statement to reflect that - an error statement. 

The 'Note' bit means that having an empty string is NOT the same as 'nil'. As it is a valid string, it is different to nil. But it's not appropriate to have an empty string - that would not be an animal's name!

For the next bit, I'm not going to copy and paste anything, just type out a summary - 

Failable Initializations for Enums...

So the example here is with temperature - having Fahrenheit, Celsius and Kelvin - for each case, returning the appropriate letter. Then for the default (anything else) returning nil. 

Raw Values - if you specify the type (like we did with Int in our example), then we can skip the switch statement and default value. 

Right, so I've gleaned a couple of things here - 

  • Specifying the type for Enums means that you have finite options for values. Anything else will return 'nil' - an optional. This is safer and easier than typing out the optional chaining. 
  • For structs/classes, you need to use optional binding/chaining so that you can deal with cases where no value is used - the compiler would crash otherwise. 
That's about it and I have five minutes left....

Initializer Delegation

struct Point {
    var x: Int = 0
    var y: Int = 0
}

struct Size {
    var width: Int = 0
    var height: Int = 0
}

struct Rectangle {
    var origin = Point()
    var size = Size()
    
}

As these are structs, we have member-wise inits for them - we can create an instance with the parameters already there for us. Same with Rectangle. We'd only need to do Rectangle().

struct Rectangle {
    var origin = Point()
    var size = Size()
    
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }

So, this custom initializer is created. Value (struct/enum) and Reference (class) types, can have multiple initializers. 

Another init inside the Rectangle struct:

init(x: Int, y: Int, height: Int, width: Int) {
        let origin = Point(x: x, y: y)
        let size = Size(width: width, height: height)
        
        self.init(origin: origin, size: size)
    }

This bit here - self.init(origin: origin, size: size)

This was instead of using self.origin - origin; self.size - size again. 

Basically, we can create multiple inits. I don't get the point of this. 

init(center: Point, size: Size) {
        let originX = center.x - size.width/2
        let originY = center.y - size.height/2
        let origin = Point(x: originX, y: originY)
        self.init(origin: origin, size: size)
    }

Right I'm going to try out these inits to try to make sense of them. 

let rectangle4 = Rectangle(x: 3, y: 4, height: 5, width: 6)

This was the only one I could put any sorts of values into!

I wish in a way that I had stopped after the 'failable' lessons and research. It's now 12:30 (10 mins over) and I honestly don't see the point of doing these initializers if you can't actually create an instance with them - apart from the second one!

So, there we go for today. Frustrating stuff as I don't see the point in a lot of it. Hopefully it will come together! Will leave it here and hope that next time it starts clicking into place. If not, I will have to do more reading outside of this course to make sense of it. All part of the learning process.


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)