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 Build a Weather App with Swift Simple Data Structures Loading Files From A Directory

Caleb Kleveter
MOD
Caleb Kleveter
Treehouse Moderator 37,862 Points

Loading files from a directory code challenge.

I am attempting to finish the loading files from a directory code challenge. Here are the instructions:

'Given a resource named "CrazyInformation.plist" located in the main application directory, first retrieve the path to this resource and assign the string to a constant named plistPath. Once you have the plistPath, create an instance of NSDictionary containing the contents of the plist.'

But I get this error on the second constant:

Bummer! Don't forget to instantiate the dictionary with the contents of the plist!.

This is the error in the preview:

swift_lint.swift:8:47: error: value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?
let dictionary = NSDictionary(contentsOfFile: plistPath)
                                              ^
                                                       !
plist.swift
import Foundation

// Add your code below

let plistPath = NSBundle.mainBundle().pathForResource("CrazyInformation", ofType: "plist")
let dictionary = NSDictionary(contentsOfFile: plistPath)

1 Answer

Michael Hulet
Michael Hulet
47,912 Points

TL;DR: pathForResource:ofType: returns an optional, and you need to unwrap it.

The problem here lies in Swift's love of over-safety. It has a concept of "Optionals", where a variable could either contain a value or nil. To find out, you have to "unwrap" it. This could be done in one of two ways.

The Easy Way

The fastest is to explicitly unwrap it is inline with an exclamation point. Be careful, though, as if it actually does contain nil, your app will crash. It's probably ok in this case, since it should never be nil, but you should form a habit of using the other way, which I'll show you in a second. In the meantime, explicitly unwrapping it would make your code look like this:

// This constant is inferred to be a String? (an optional String)
let plistPath = NSBundle.mainBundle().pathForResource("CrazyInformation", ofType: "plist")
// Notice the ! after plistPath. This unwraps the optional to get to the value inside
let dictionary = NSDictionary(contentsOfFile: plistPath!)

The Better Way

There's a better way to do this, though. That would be to implicitly unwrap it, which is known as "if let" syntax. This is better because it's safer. If the value contains nil, your app won't crash, and you can run some alternate code, instead. Using if let syntax extracts the value out of the optional and assigns it to its own temporary constant which is only available within the scope of the if let statement. Implicitly unwrapping it would look like this:

// This constant is inferred to be a String? (an optional String)
let plistPath = NSBundle.mainBundle().pathForResource("CrazyInformation", ofType: "plist")
// This will extract the String value from the optional and assign it to path
if let path = plistPath{
    let dictionary = NSDictionary(contentsOfFile: path)
}
Caleb Kleveter
Caleb Kleveter
Treehouse Moderator 37,862 Points

Neither of the code snippets are passing. I am getting the error Bummer! Store the path to the resource in a constant named plistPath. I tried to change some stuff to fix it, but nothing worked.

Michael Hulet
Michael Hulet
47,912 Points

I didn't test the snippet I posted in my actual answer, but a slightly adapted version of "The Better Way" seems to work. The only difference is that it shoves the creation of the plistPath constant into the if let statement, and gets rid of the extraneous path constant entirely. Try this:

// This does the same thing as my last answer, but unwraps and assigns plistPath in one line, without using an extra constant
if let plistPath = NSBundle.mainBundle().pathForResource("CrazyInformation", ofType: "plist"){
    let dictionary = NSDictionary(contentsOfFile: plistPath)
}