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

Ian Bowers
Ian Bowers
3,665 Points

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

    }
}
Luke Bearden
Luke Bearden
15,597 Points

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
}
Jesse DeOms
Jesse DeOms
18,815 Points

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 :)

Edward Sapp
Edward Sapp
8,479 Points

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!

Evans Attafuah
Evans Attafuah
18,277 Points

swift 3.0 requires let for unwrapping multiple optionals

Josh Rondestvedt
Josh Rondestvedt
3,830 Points

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

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

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...

Abdoulaye Diallo
Abdoulaye Diallo
15,863 Points

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"]
    }
}
Ian Bowers
Ian Bowers
3,665 Points

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!!

Nick Mauro
Nick Mauro
3,944 Points

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.

Nick Mauro
Nick Mauro
3,944 Points

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

Nick Mauro
Nick Mauro
3,944 Points

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.

Tong Wang
Tong Wang
4,800 Points

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?

Brian Habecke
Brian Habecke
2,287 Points

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...)

justinm
justinm
10,385 Points

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.

Brian Habecke
Brian Habecke
2,287 Points

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!

Logan Cornelius
Logan Cornelius
16,876 Points

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.