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 (Retired) Pulling Data From the Web Making a Network Call

If you're having trouble with this section of the course, it's not your fault.

It appears as though this course has not yet been updated to conform to the latest iteration of Swift. There are several issues with the code as it stands and I'm sure the Treehouse team is working hard to address them as quickly as possible.

There seems to be some issue surrounding the NSURL(string:, relativeToURL:) method as it returns an optional and is not actually constructing the URL object we're asking it to. You'll notice in the documentation that, in the event that this initializer fails, it returns nil. I replaced the coordinates with a simple string ("hello"), and it returned an object as it should, but of course that won't work for the course. I believe this has something to do with the fact that our URL contains a space and therefore needs to be encoded with NSUTF8StringEncoding (replaces all spaces with "%20").

Additionally, the dataWithContentsOfURL:options:error: method has been deprecated in Swift and replaced with the initializer NSData(contentsOfURL:, options:, error:). Go ahead and use that one for the time being.

In the meantime, this code works fine (I understand it's not perfect, but it works for now):

class ViewController: UIViewController {

    private let apiKey = "yourAPIKeyHere"
    private let locationPoints = "37.8267, -122.423"

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let baseURL = "https://api.forecast.io/forecast/"
        let encodedPoints = locationPoints.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! // This method returns an optional, we need to force unwrap

        if let forecastURL = NSURL(string: "\(baseURL)\(apiKey)/\(encodedPoints)") { // We have to optionally bind because this method returns an optional and we need to act on the value it returns.

            let weatherData = NSData(contentsOfURL: forecastURL, options: nil, error: nil)
            println(weatherData)

        }

    }

}

I'm sure this can be improved, but I hope it helps a few folks.

It returns nil

Richard Price
Richard Price
2,050 Points

this code works thanks for this I'm sure they ll sort the issue pretty quickly I would have thought

Edwin Capel
Edwin Capel
2,232 Points

thanks, this really helped !

Rune Rapley-Møller
Rune Rapley-Møller
Courses Plus Student 24,411 Points

Thx this really helped. I'm still not sure why it returned a space in the url string? Is it because it returns an optional?

7 Answers

Pasan Premaratne
STAFF
Pasan Premaratne
Treehouse Teacher

Hey everyone!

From the project files supplied along with this video, I was able to make two small changes to get the project to run and return the same results as the video.

let weatherData = NSData(contentsOfURL: forecastURL!, options: nil, error: nil)

First, I used NSData(contentsOfURL rather than NSData.dataWithContentsOfURL. Second, I unwrapped the forecastURL optional. Ramon and Jake are doing a great job pointing you guys in the right direction and in fact the usage of the optional binding in their code samples is safer so please use that. Great job guys :)

This post does bring up a larger issue though. This is a screenshot from the Xcode 6.1 release notes. Swift is brand new and Apple is iterating on it very fast. With every iteration of Swift and Xcode, things willchange. As the screenshot indicates, lots of changes were made to Foundation (the framework where NSString, NSData and NSURL come from) as well as UIKit and many others. That is why issues like this are coming up. NSData(contentsOfURL:) did not exist when I wrote the course, but now the older method has been deprecated.

Pay specific attention to the last line in the screenshot - This audit effort is ongoing which means that it may change again.

Unfortunately, issues like the one this post raises, will come up again until Swift and Cocoa are finally audited all the way through. This is just how Swift is going to be for at least another year. For things like having to unwrap the result of a method, Xcode is actually quite helpful and will suggest a fix automatically. For other things like deprecated methods, usually if you option + click on the method, you can check out the docs and they point you in the right direction.

The iOS community here on Treehouse is doing a great job of letting everyone know how to go around these changes and I'll let the mods know as well. Amit and I are still figuring out how to go about this. We didn't expect Apple to break so many things with every single release.

Hope this helps!

Hey Prasan,

Question:

What will this mean to my applications? Will my applications still work on mobile phones if I write code now, and the syntax or code gets deprecated?

Greetings,

Simon

Pasan Premaratne
Pasan Premaratne
Treehouse Teacher

Simon Rood,

It really depends. Here's a good answer on Stack Overflow regarding the same question.

Nick Calabro
Nick Calabro
16,335 Points

I've either been staring at code for too long and just can't see my problem here, or I'm still missing something

        // Do any additional setup after loading the view, typically from a nib.
        let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
        let forecastURL = NSURL(string: "40.713959, -74.002036", relativeToURL: baseURL)
        //let weatherData = NSData.dataWithContentsOfURL(forecastURL!, options: nil, error: nil)
        let weatherData = NSData(contentsOfURL: forecastURL!, options: nil, error:nil)
        println(weatherData)

When I try to debug, forecastURL is nil. Why is it nil?? I tried making baseURL an optional in my let forecastURL declaration, but that seemed to have no effect.

Nick, eliminate the comma in the string "40.713959, -74.002036" or escape the space in the URL with %20. URLs don't work with spaces.

Jose A Ocando Jose A Ocando
Jose A Ocando Jose A Ocando
1,482 Points

Jake,

I'm sorry to say that your solution returns a nil in mine as well. What's crazy is that that the playground returns a nil, but when I execute the program, the console log does seem to show the data. But, when I input the data into jsonpretty.com (next video), it doesn't recognize the data as JSON?

Treehouse,

I think we need your HELP!

This will give me the following error: "Bound value in a conditional binding must be of Optional type" I did try to fix it by using the following code:

 // Do any additional setup after loading the view, typically from a nib.

        let baseURL = NSURL(string: "https://api.forecast.io/forecast/")
        let encodedPoints = location.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! // This method returns an optional, we need to force unwrap

        let forecastURL = NSURL(string: encodedPoints, relativeToURL: baseURL)

        // We have to optionally bind because this method returns an optional and we need to act on the value it returns.

        let weatherData = NSData(contentsOfURL: forecastURL, options: nil, error: nil)

        println(weatherData)

But this code will give me the following error: Thread 1: EXC_BAD_ACCESS (code = 1, acces = 0x0)

Which version of Xcode are you running? NSURL(string:) should return an Optional (as of 6.1), so that's odd!

Ramon Bonte
Ramon Bonte
1,981 Points

Add your API-key to the URL and add an ! to forecastURL in let weatherData to unwrap it.

Thank you Ramon, that worked. :)

Here is my updated (working) code:

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(forecastAPIKey)/")

        let encodedPoints = location.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! // This method returns an optional, we need to force unwrap

        let forecastURL = NSURL(string: encodedPoints, relativeToURL: baseURL)
        let weatherData = NSData(contentsOfURL: forecastURL!, options: nil, error: nil)

        println(weatherData)

    }

I tried adding the API-key and ! to forecastURL but still no luck. I have the following code in a playground but still getting an error.

2014-10-26 22:43:54.780 jsonPractice[1839:77626] NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9807) nil

import UIKit
import Foundation

let apiKey = "b8d132a872db1f7ad18a2d6d49994e06"
let locationPoints = "51.5073,-0.1274"

let baseURL = "https://api.forecast.io/forecast/b8d132a872db1f7ad18a2d6d49994e06"
let encodedPoints = locationPoints.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! // This method returns an optional, we need to force unwrap


if let forecastURL = NSURL(string: "\(baseURL)/\(encodedPoints)") {

let weatherData = NSData(contentsOfURL: forecastURL, options: nil, error: nil)

    println(weatherData) }
Ramon Bonte
Ramon Bonte
1,981 Points

Hi Grace, try to add a / after your api-key in let baseURL. that should fix your problem

Eric Whittaker
Eric Whittaker
2,974 Points

let weatherData = NSData(contentsOfURL: forecastURL!, options: nil, error: nil)

that needs to be your <weatherData> constant definition.

Nick Calabro -> remove the space in your string:

"40.713959,-74.002036"```
Nick Calabro
Nick Calabro
16,335 Points

Such is life! how trivial. Thank you

William Crawford
William Crawford
4,319 Points

Pasan, With Ramon & Jake's fix, I got thru the JSON video fine. But, the Challenge Task 1 of 1 is still looking for "NSData.dataWithContentsOfURL" instead of "NSData(contentsOfURL". I will skip the Challenge for now and go on to the next video.
Thanks.

Pasan Premaratne
Pasan Premaratne
Treehouse Teacher

Thanks for the heads up. We've got a bit of infrastructure changes to make to fix this challenge so it should be updated soon.