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! While you're at it, check out some resources Treehouse students have shared here.

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

please help with my code

In the editor, you have a struct named Book which has few stored properties, two of which are optional.

Your task is to create a failable initializer that accepts a dictionary as input and initializes all the stored properties. (Hint: A failable init method is one that can return nil and is written as init?). Name the initializer parameter dict.

Use the following keys to retrieve values from the dictionary: "title", "author", "price", "pubDate"

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

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

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

    }
}

2 Answers

You can't unwrap and assign optionals to a property of a class directly. You basically have to use intermediate variables, in this case I used title and author. Moreover, you are redefining all variables by putting a let in front of them, which is not what you want and one reason why the code throws errors.

This example works, however you haven't included the link to the challenge, so I a could not check if it passes.

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

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

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

Hope that helps :)

P.S. I strongly encourage everyone to use Xcode Playgrounds to write and test your code. Xcode does indicate errors right away and offers solutions to fix them. Not to mention autocompletion ;)

thank you it passes, may i ask why you write self.author = author and self.title = title again at the bottom again even if they are not optional types in the struct?

Optionals are unwrapped with the let unwrappedValue = optionalValue construct. Whereas optionalValue can be either nil or a value, as soon as you unwrap it is guaranteed to have a value, provided optionalValue is not nil. In case of guard, the code after the guard statement is only executed if optionalValue is not nil:

guard let unwrappedValue = optionalValue else {
    // optionalValue is nil
    print("optionalValue can not be assigned to unwrappedValue")
    return
}

// unwrappedValue is now available in this scope
print(unwrappedValue)

If you want to continue execution of the code, the following would do:

if let unwrappedValue = optionalValue {
   // unwrappedValue is not nil, and only available within
   // the scope of the if body 
   print(unwrappedValue)
}

So, to answer your question: I can only assign the unwrapped values to title and author, as they are not optional, indicated by the absence of ? after the type. price and pubDate however are optionals, indicated by the ?, that is they can be nil.

A dictionary always returns an optional for a key, as the key might not exist and thus there can be no value for it. Thats why you can directly assign it to price and pubDate.

Hope that makes sense :)