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 trialJake Johnson
6,839 PointsIf 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.
Richard Price
2,050 Pointsthis code works thanks for this I'm sure they ll sort the issue pretty quickly I would have thought
Edwin Capel
2,232 Pointsthanks, this really helped !
Rune Rapley-Møller
Courses Plus Student 24,411 PointsThx 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
Treehouse TeacherHey 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!
Simon Rood
1,096 PointsHey 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
Treehouse TeacherIt really depends. Here's a good answer on Stack Overflow regarding the same question.
Nick Calabro
16,335 PointsI'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.
Dave Fontenot
8,170 PointsNick, 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
1,482 PointsJake,
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!
Simon Rood
1,096 PointsThis 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)
Jake Johnson
6,839 PointsWhich version of Xcode are you running? NSURL(string:) should return an Optional (as of 6.1), so that's odd!
Ramon Bonte
1,981 PointsAdd your API-key to the URL and add an ! to forecastURL in let weatherData to unwrap it.
Simon Rood
1,096 PointsThank 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)
}
Grace Copplestone
3,779 PointsI 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
1,981 PointsHi Grace, try to add a / after your api-key in let baseURL. that should fix your problem
Eric Whittaker
2,974 Pointslet weatherData = NSData(contentsOfURL: forecastURL!, options: nil, error: nil)
that needs to be your <weatherData> constant definition.
Simon Rood
1,096 PointsNick Calabro -> remove the space in your string:
Nick Calabro
16,335 PointsSuch is life! how trivial. Thank you
William Crawford
4,319 PointsPasan,
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
Treehouse TeacherThanks for the heads up. We've got a bit of infrastructure changes to make to fix this challenge so it should be updated soon.
Harminder Virk
138 PointsHarminder Virk
138 PointsIt returns nil