Treehouse Intermediate Course Part 7 (Extensions)
Getting some consecutive day entries in feels good. I'm pleased that I'm able to continue to learn a little bit here and there. The other day I felt a pang of frustration that I wasn't making leaps and bounds of progress and moving on to becoming an app developer, but unless I dedicated myself to it full time, this is NOT a target in the near future! It is a long term aim and there is no point rushing that. I'm getting the syntax and concepts into my long term memory this way. At some point I'll do a list of aims, but for now it is simply to complete the Treehouse Intermediate course, and at that point, pause my subscription and seek out some more practical challenges from Udemy. Last time it was about type methods and final classes - all that seemed ok. The first part for today is is about 'extending a native type' then on to protocols...
Extending a Native Type
Pasan threatens to 'blow my mind' - easy tiger! So adding minor functionality on top of an existing class...that must be extensions!
So extensions are adding extra functionality to an existing class, structure, enum or protocol type. We can even do this for classes we don't have the source code for.
Extending a Native Type
Pasan threatens to 'blow my mind' - easy tiger! So adding minor functionality on top of an existing class...that must be extensions!
So extensions are adding extra functionality to an existing class, structure, enum or protocol type. We can even do this for classes we don't have the source code for.
extension Int {
var isOdd: Bool {
return self % 2 != 0
}
}
3.isOdd
That's REALLY cool!
So some rules -
- You can add computed properties (like the above example)
- You can't add property observers (will set/did set) or stored properties *The SP part is important as it would mean all of that types inits would have to be reimplemented!
- You can add new instance and type methods
- You can define nested types (types within types)
- You can add convenience inits (not designated)
Challenge -
Your task is to extend the String type and add a method that lets you add an integer value to the string. For example:
"1".add(value: 2)
If the operation is possible, return an integer value. If the string does not contain an integer and you cannot add to it then return
nil
.
So I got some of it but missed a couple of things - creating a separate constant to temporarily store the value. Also I did += rather than +. As self is not mutable.
extension String {
func add(value: Int) -> Int? {
if let numberString = Int(self) {
return numberString + value
} else {
return nil
}
}
}
So not far off!
Protocol Conformance Using Extensions
Right so the example here is to create a protocol that can be used in a subclass.
protocol UniquelyIdentifiable {
var uuid: Int { get }
}
This would then be used in a subclass of UIView. Instead, composition is better.
extension UIView: UniquelyIdentifiable {
var uuid: Int {
return hash
}
}
So the point of this is that we have created an extension of a UIView with the hash value (inbuilt) that can be returned.
Challenge -
In the editor you have a protocol,
PrettyPrintable
along with a struct, User
.
Your task is to extend
User
to conform to PrettyPrintable
by adding a computed property that returns a formatted description of a User
instance.
Note: The specifics of the formatted description don't matter to pass this challenge.
So here's what I've got:
protocol PrettyPrintable {
var prettyDescription: String { get }
}
struct User: PrettyPrintable {
let name: String
let ID: Int
var prettyDescription: String {
return ("\(name) \(ID)")
}
}
That works for me and on Xcode, but the Treehouse code doesn't like it. Let's see what the community says...
Ahhh I didn't use extension for User! Of course. Let's try again....
Got it!
extension User: PrettyPrintable {
var prettyDescription: String {
return ("\(name) \(ID)")
}
}
Protocol Extensions
So random number generator is a protocol. Didn't actually realise that! Ah it's not - arc4random is of course a function. Right...
Right got the point of this now, we are extending the protocols!
Here is the example we're using....
protocol RandomNumberGenerator {
func random() -> Int {
}
Now we will be using the extension keyword to extend this....and use the arc4random!
extension RandomNumberGenerator {
func random() -> Int {
return Int(arc4random())
}
}
class Generator: RandomNumberGenerator {}
let generator = Generator()
generator.random()
Cool! I didn't need to add anything to the class to use the generator function.
protocol Person {
var firstName: String { get }
var lastName: String { get }
var fullName: String { get }
}
extension Person {
var fullName: String {
return firstName + " " + lastName
}
}
Right a couple of bits. All of the properties are computed properties in the Person protocol. The extension makes fullName be combined....
struct User2: Person {
var firstName: String
var lastName: String
}
let newName = User2(firstName: "Lance", lastName: "Stroll")
newName.fullName
I get this but couldn't I have just done this with a gettable computed property of fullName? Let's just confirm....
struct User3 {
var firstName: String
var lastName: String
var fullName: String {
get {
return firstName + " " + lastName
}
}
}
Yes so that would work. So I don't see the benefit of the protocol bit with this - I guess the idea is just to understand how I could create a protocol, extend it, then use it in a struct.
Method Dispatch in a Protocol Extension
So we can apparently add requirements not specified in the original protocol for the extension and a default implementation too.
extension Person {
var fullName: String {
return firstName + " " + lastName
}
func greeting() -> String {
return ("Hi there! My name is \(fullName)")
}
}
So we've added a method along with an implementation to the extension of the protocol.
Right so I've added the same function to the User2 struct, with a slightly different string, then created an instance, using the Person protocol of type User2...
let anotherUser: Person = User2(firstName: "Steve", lastName: "Sherman")
If I call the function on it, it will use the Person one, not the User2 one! The protocol not the struct. Why is this?
Ahhh it's because the Protocol is the BASE class. OR the SUPER class! That makes sense. It works with mixed types (protocol/struct) in this case.
Another point is that even in the extension, the protocol will take priority. It will use the default one. There is a lot of technical stuff here which I am skimming over - all very confusing but good to get some exposure to it!
Implementations stay the same....deciding when to use the extension of the protocol for adding in a function etc. It could avoid bugs.
Challenge -
In the editor, you've been provided with a protocol,
Calculator
, that contains a single requirement - a method that computes the square of a value.
Your task is to provide a default implementation for the method through a protocol extension.
protocol Calculator {
func square(_ value: Double) -> Double
}
// Enter your code below
extension Calculator {
func square(_ value: Double) -> Double {
return value * value
}
}
Did it! Only thing I was unsure of was writing out the func again but it worked.
The quiz at the end confused me - one question was wrong with the answer. Looking at some more documentation, it is clear that if you specify an instance to a protocol, then declare it as a struct, then it will adopt the protocol's behaviour, not the struct for a function. That does make sense but this incorrect answer on the Treehouse quiz really through me! So, all of extensions complete! Some good stuff here, learning something new and all. That will do for today!
Comments
Post a Comment