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 Swift 2.0 Enumerations and Optionals Introduction to Optionals Initializing Optional Values

creating a Struct init?() and accepting a dictionary type

I am working on this problem:

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 an 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? (title: String, author: String, price: String?, pubDate: String?
init?(dict: [String:String]) 
init?(NSDictionary) ??

I've tried declaring each individual constant like up above and before that I basically copied

struct Friend {
    let name: String
    let age: String
    let address: String?

}

func createFriend(dict: [String: String]) -> Friend? {
    guard let name = dict["name"], age = dict["age"] else {
        return nil
    }

    let address = dict["address"]

    return Friend(name: name, age: age, address: address)
}

and it wouldn't compile

11 Answers

Hi Ian,

I think the Challenge is looking for something more like this;

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"]

    }
}

why do title and author need to be set before setting them to self.title and self.author? Why doesn't the following work?

guard let self.title = dict["title"], self.author = dict["author"] else {
  return nil
}

Thanks Evan for your help with this (and thanks Ian for posting this question)! I was very close on my own and seeing your work here was really helpful :)

I'm confused--wouldn't this crash if there's no value for price & pubDate? Shouldn't you also protect those with an "if let" or a "guard" statement?

Hi Edward, price and pubDate are optional, as indicated by the question mark. Optionals can either have a value or be nil. Because the init needs to be failable, it too has a question mark, and handles the two non-optional properties using a guard statement.

Hope that helps!

swift 3.0 requires let for unwrapping multiple optionals

in regards to the guard let title, add let before author.

          guard let title = dict["title"], let author = dict["author"] else {
            return nil

add let let author.....

This code is correct except for a missing 'let' keyword before 'author' in the guard statement. (At this time [Swift 4], the 'let' keyword is required for use with multiple declarations in guard statements.)

See full code / answer below...

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"]

}

}

Actually Nick Mauro is correct (thanks, Nick!) — at this time (Swift 4) — the 'let' keyword is required each time with multiple/inline declarations in guard statements.

When the above code by Evan Demaris was provided, it was also correct, but now requires the addition of a second 'let' keyword to pass. (In this example, if you try removing the 'let' keyword before the 'author' declaration in the guard statement, you should receive a compiler error in Xcode.)

A further explanation is provided in the answers for this question on Stack Overflow.

Updated answer to this challenge shown below:

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"]
    }
}

I'm an idiot I don't know why I was forcing a return statement I was trying anything and everything to get it to work. Thanks!!

warp42 you and Even are missing a let before author. Try:

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"]
    }
}

As you are declaring multiple constants inline, the second let is assumed.

I believe that is no longer the case in Swift 3.

I believe that is no longer the case in Swift 3.

This question was asked over a year ago regarding iOS > Swift 2.0 Enumerations and Optionals > Introduction to Optionals > Initializing Optional Values; Swift 3 syntax wouldn't be relevant in this context.

struct Book { let title: String let author: String let price: String? let pubDate: String? init? (dict: [String: String]){ self.title = dict["title"]! self.author = dict["author"]! self.price = dict["price"] self.pubDate = dict["pubDate"] }

} There is no complier error here when I add "!" . I just wondering what might go wrong with this code?

I keep getting a "your code could not be compiled, even by cutting and pasting the above example by Evan. When switching to the editor, no errors are shown. What gives?

I'm not sure what's causing that problem; you're deleting the original code before pasting in mine, right?

Maybe it's the formatting? Try the code below (I just successfully completed the test with this code...)

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"]
        }    
}

(honestly, it looks identical though...)

Sometimes I get this issue if there is too many/not enough brackets. When you copy and paste the code try to make sure the brackets match up properly and that there isn't an extra one from the old code.

I have no clue what happened there, but it looks like the browser wasn't resetting properly. Quit out yesterday, logged in again today, reposted the correct answer, it worked. Thanks for the help though!

I'm having a hard time understanding how this plays out in real case scenario. I'm setting these all up for a safe guarded initializer, but when I actually initialize the dictionary I only have the [String:String]. Getting a little confused over all this.