Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

iOS Error Handling in Swift 2.0 Error Handling Handling Errors

Rich Valiant
Rich Valiant
2,417 Points

Misunderstanding throws function inside struct which takes dictionary of optional value?

I'm having huge problems with this coding challenge and I don't feel like there is a literal example in the course.

My Problem

This code below is what I finally got to work after days of trial, but I still don't understand it.

Here are my questions?

  • why is there a question mark on the value of the key/value pair and also on the outside of the type declaration [String:String?]? (I understand the one inside, but both?

  • if I am using the guard keyword/clause, why do I still have to add a question mark to the data variable which holds the dictionary containing the optional value guard let key = data?["someString"]

  • this code below works for the treehouse compiler but in xcode, the second guard statement guard let value = key brings up a value was not defined, consider replacing with boolean fix?

Lastly

I'm not even sure if I truly accomplished the challenge since the hint portion of the challenge summary suggested:

Hint: To get a list of keys in a dictionary you can use the keys property which returns an array. Use the contains method on it to check if the value exists in the array

Can someone show me a version completing this task with the suggested hint principles. I'm utterly lost with that.

Thanks in advance to whomever is able to help! I greatly appreciate it. I've read docs, I've watched course over and over and tried google, but I'm still at a loss on this one!

error.swift
enum ParserError: ErrorType {
  case EmptyDictionary
  case InvalidKey
}

struct Parser {
  var data: [String : String?]?

  func parse() throws {
    guard let key = data?["someKey"] else {
      throw ParserError.InvalidKey
    }
    guard let value = key else {
      throw ParserError.InvalidKey
    }
  }
}

let data: [String : String?]? = ["someKey": nil]
let parser = Parser(data: data)

1 Answer

Taylor Amsler
Taylor Amsler
4,427 Points

Hi Rich. Good questions (and excellently formatted). Usually I try to lead a person to the right answer, but here I'm just going to write it in full because your questions are more conceptual and you have clearly tried to get it right. Let me see if I can take you step by step through my logic when answering this challenge.

1) Why is there a question mark on the value of the key/value pair and also on the outside of the type declaration [String:String?]? (I understand the one inside, but both?)

So this is fundamental to the challenge and to more complicated coding. Here there are several possible ways that you could fail to retrieve a value. We are looking at two in particular: 1) You could retrieve a dictionary without data [:]?. 2) You could retrieve a dictionary without your data (you don't have the key) [:?].

2) If I am using the guard keyword/clause, why do I still have to add a question mark to the data variable which holds the dictionary containing the optional value guard let key = data?["someString"]

You don't. At least, in the way that I wrote it you don't.

3) This code below works for the treehouse compiler but in xcode, the second guard statement guard let value = key brings up a value was not defined, consider replacing with boolean fix?

So for this last one, you are writing "guard let value". This tells the compiler that you are declaring a new property: "value". This property has not been initialized and, in this case, there is no real reason to declare a new value.

enum ParserError: ErrorType {
    case EmptyDictionary
    case InvalidKey
}

struct Parser {
    var data: [String : String?]?

    func parse() throws {
        guard data != nil else {
            throw ParserError.EmptyDictionary
        }
        guard data!.keys.contains("someKey") else {
            throw ParserError.InvalidKey
        }
    }
}

let data: [String : String?]? = ["someKey": nil]
let parser = Parser(data: data)

So this is how I structured mine. I checked to see if the dictionary had no data (guard data != nil). Then I checked to see if the key to the data contained "someKey". The methods "keys.contains" are built-in, I did not write those methods myself.

Additional point of interest. You will see that I force unwrapped "data" in the second throw. The reason I did this was because I already checked to see if the value of dictionary was nil using the first throw. Therefore, because it passed the check, I thought it would be safe to force unwrap. I'm not experienced enough to say whether that will always be the right choice, but I hope you appreciate my logic.

Thanks and sorry for the long explanation!

Rich Valiant
Rich Valiant
2,417 Points

Hey Taylor,

First of all, thanks for the kudos on the writing style. I've been working on that recently for my online publishing projects. Also, no apologies necessary for your extensive response. It was required and so much appreciated. Thanks for taking out the time to make sure I understood. Lastly, thanks for such an immediate response. I'm only now getting back to you because I was heavily researching other methods based upon your code and what it illuminated for me!

I wonder if you'll check my code below and tell me what you think?

I loved your approach and it immediately made sense but I was unnerved at the fact that we still had to use the bang operator on the second guard check. My frustration wasn't with you or your answer, but again, I felt a little frustrated with the course and seemingly feeling like we had a quiz whose answer we had not bee properly prepared for. In the courses, they typically say that we don't ever have to use the bang operator. Which left me feeling like, there had to be another way. I went through the course 3 times and still couldn't come up with a different way other than you proposed. Then I started fiddling and experimenting in Xcode. Forcing the compiler to keep giving me different suggestions. I found this to work guard let someData = data where someData.keys.contains("someKey"), but I am not sure of the logic as I haven't been taught this way as of yet.

Here's my code below:

enum ParserError: ErrorType {
    case EmptyDictionary
    case InvalidKey
}

struct Parser {
    var data: [String : String?]?

    func parse() throws {
        guard data != nil else {
            throw ParserError.EmptyDictionary
        }
        guard let someData = data where someData.keys.contains("someKey") else {
            throw ParserError.InvalidKey
        }
    }
}

let data: [String : String?]? = ["someKey":nil]
let parser = Parser(data: data)

do {
    try parser.parse()
} catch let error {
    print("No data!")
}

It absolutely works, but I wasn't sure if that was a proper way to use guard and where after the assignment operator. Let me know your thoughts on this. If its completely in proper use case, than this would final be an alternative way to not have to force unwrap.

Thanks again!