Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community!

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Swift 2.0 Enumerations and Optionals Introduction to Optionals Initializing Optional Values

mahi
mahi
7,137 Points

can't figure this one out!!

optionals.swift
struct Book {
    let title: String
    let author: String
    let price: String?
    let pubDate: String?

    init? (dict: [String: String]){
         guard self.title = dict["title"], self.author = dict["author"] else{
            return nil
        }

         self.price = dict["price"]
         self.pubDate = dict["pubDate"]
     }
}

1 Answer

Martin Wildfeuer
PLUS
Martin Wildfeuer
Courses Plus Student 11,071 Points

You cannot assign to self with a guard statement in an initializer. You can assign to "temporary" variables before assigning to properties, though:

struct Book {
    let title: String
    let author: String
    let price: String?
    let pubDate: String?

    init? (dict: [String: String]){
        guard let title = dict["title"], let author = dict["author"] else {
            return nil
        }

        self.title = title
        self.author = author
        self.price = dict["price"]
        self.pubDate = dict["pubDate"]
    }
}

Hope that helps :)

mahi
mahi
7,137 Points

Thanks a lot!!! That helped.

William Hartvedt Skogstad
William Hartvedt Skogstad
5,857 Points

Why do we need to declare the self statements for the 4 constants at the end of the initializer?

mahi
mahi
7,137 Points

@ William Hartvedt Skogstad we declare those to initialize them. first we take out the values from all the keys and then we initialise all the properties.

Martin Wildfeuer
Martin Wildfeuer
Courses Plus Student 11,071 Points

Unwrapping values depends on the if/guard let unwrappedValue = wrappedValue construct (not talking about forced unwrapping!). Therefore, it is not possible to assign an unwrapped value to a previously declared constant/variable directly. To work around this, we can use temporary variables. In my example, I chose to to use the same temporary constant names as the ones of the properties of the Book struct.

These temporary constants are only available within the scope of the init method. Using the same name has the effect of shadowing the property of the class, that is, referring to title within the initializer will give you the constant I declared there and you can't access the property of your Book struct named title directly. To access the property, we have to prefix it with self. explicitly, so Swift knows that we refer to the property of the struct, not the constant defined within the initializer.

struct Book {
    let title: String
    let author: String
    let price: String?
    let pubDate: String?

    init? (dict: [String: String]){
        guard let title = dict["title"], let author = dict["author"] else {
            return nil
        }

        // title = title would not work, as it's ambiguous
       // The compiler thinks we want to assign title to itself
        self.title = title 
        self.author = author // same here

        // We don't need self here, as it is not ambiguous.
        // We are assigning the value directly to the property
        price = dict["price"] 
        pubDate = dict["pubDate"] // same here
    }
}

Although I used self for all the properties we wanted to initialize in my original answer, it is in fact only needed for the shadowed variables. Self should only be used where it is really needed, so my original answer was correct but not considered best practice. I just wanted to avoid confusion :)

I know that was a lot information, I hope it helps, though :)