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
Paul M
16,370 PointsI have error 20 running my Stormy app, can someone help? URGENT
I have no idea why but when I run the app it just gives my error 20 or "UnexpectedResponseError". Can anyone tell me why it says this, is it something on my end? Network Programming With Swift 2.0
I have nothing in the console and here is my code
APIClient.swift
import Foundation
public let TRENetworkingErrorDomain = "com.treehouse.Stormy.Networking"
public let MissingHTTPResponseError: Int = 10
public let UnexpectedResponseError: Int = 20
typealias JSON = [String : AnyObject]
typealias JSONTaskCompletion = (JSON?, NSHTTPURLResponse?, NSError?) -> Void
typealias JSONTask = NSURLSessionDataTask
enum APIResult<T> {
case Success(T)
case Failure(ErrorType)
}
protocol JSONDecodable {
init?(JSON: [String : AnyObject])
}
protocol Endpoint {
var baseURL: NSURL { get }
var path: String { get }
var request: NSURLRequest { get }
}
protocol APIClient {
var configuration: NSURLSessionConfiguration { get }
var session: NSURLSession { get }
func JSONTaskWithRequest(request: NSURLRequest, completion: JSONTaskCompletion) -> JSONTask
func fetch<T: JSONDecodable>(request: NSURLRequest, parse: JSON -> T?, completion: APIResult<T> -> Void)
}
extension APIClient {
func JSONTaskWithRequest(request: NSURLRequest, completion: JSONTaskCompletion) -> JSONTask {
let task = session.dataTaskWithRequest(request) { data, response, error in
guard let HTTPResponse = response as? NSHTTPURLResponse else {
let userInfo = [
NSLocalizedDescriptionKey: NSLocalizedString("Missing HTTP Response", comment: "")
]
let error = NSError(domain: TRENetworkingErrorDomain, code: MissingHTTPResponseError, userInfo: userInfo)
completion(nil, nil, error)
return
}
if data == nil {
if let error = error {
completion(nil, HTTPResponse, error)
}
} else {
switch HTTPResponse.statusCode {
case 200:
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String : AnyObject]
completion(json, HTTPResponse, nil)
} catch let error as NSError {
completion(nil, HTTPResponse, error)
}
default: print("Recieved HTTP Response: \(HTTPResponse.statusCode)- not handled")
}
}
}
return task
}
func fetch<T>(request: NSURLRequest, parse: JSON -> T?, completion: APIResult<T> -> Void) {
let task = JSONTaskWithRequest(request) { json, response, error in
guard let json = json else {
if let error = error {
completion(.Failure(error))
} else {
// TODO: Implement Error Handling
}
return
}
if let value = parse(json) {
completion(.Success(value))
} else {
let error = NSError(domain: TRENetworkingErrorDomain, code: UnexpectedResponseError, userInfo: nil)
completion(.Failure(error))
}
}
task.resume()
}
}
And here is my ViewController.swift
import UIKit
extension CurrentWeather {
var temperatureString: String {
return "\(Int(temperature))º"
}
var humidityString: String {
let percentageValue = Int(humidity * 100)
return "\(percentageValue)%"
}
var percipitationProbabilityString: String {
let percentageValue = Int(percipitationProbability * 100)
return "\(percentageValue)%"
}
}
class ViewController: UIViewController {
@IBOutlet weak var currentTemperatureLabel: UILabel!
@IBOutlet weak var currentHumidityLabel: UILabel!
@IBOutlet weak var currentPrecipitationLabel: UILabel!
@IBOutlet weak var currentWeatherIcon: UIImageView!
@IBOutlet weak var currentSummaryLabel: UILabel!
@IBOutlet weak var refreshButton: UIButton!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
lazy var forecastAPIClient = ForecastAPIClient(APIKey: "#####################") // I hashed this out, but the right one is still in there
let coordinate = Coordinate(lattitude: 37.8267, longintude: -122.423)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
forecastAPIClient.fetchCurrentWeather(coordinate) {
result in
switch result {
case .Success(let currentWeather):
dispatch_async(dispatch_get_main_queue()) {
self.display(currentWeather)
}
case .Failure(let error as NSError):
dispatch_async(dispatch_get_main_queue()) {
self.showAlert("Unable to retrieve forecast", message: error.localizedDescription)
}
default: break
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func display(weather: CurrentWeather) {
currentTemperatureLabel.text = weather.temperatureString
currentPrecipitationLabel.text = weather.percipitationProbabilityString
currentHumidityLabel.text = weather.humidityString
currentSummaryLabel.text = weather.summary
currentWeatherIcon.image = weather.icon
}
func showAlert(title: String, message: String?, style: UIAlertControllerStyle = .Alert) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: style)
let dismissAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(dismissAction)
presentViewController(alertController, animated: true, completion: nil)
}
}
Finally my ForecastClient.swift
import Foundation
struct Coordinate {
let lattitude: Double
let longintude: Double
}
enum Forecast: Endpoint {
case Current(token: String, coordinate: Coordinate)
var baseURL: NSURL {
return NSURL(string: "https://api.forecast.io")!
}
var path: String {
switch self {
case .Current(let token, let coordinate):
return "/forecast/\(token)/\(coordinate.lattitude),\(coordinate.longintude)"
}
}
var request: NSURLRequest {
let url = NSURL(string: path, relativeToURL: baseURL)!
return NSURLRequest(URL: url)
}
}
final class ForecastAPIClient: APIClient {
let configuration: NSURLSessionConfiguration
lazy var session: NSURLSession = {
return NSURLSession(configuration: self.configuration)
}()
private let token: String
init(config: NSURLSessionConfiguration, APIKey: String) {
self.configuration = config
self.token = APIKey
}
convenience init(APIKey: String) {
self.init(config: NSURLSessionConfiguration.defaultSessionConfiguration(),
APIKey: APIKey)
}
func fetchCurrentWeather(coordinate: Coordinate, completion: APIResult<CurrentWeather> -> Void) {
let request = Forecast.Current(token: self.token, coordinate: coordinate).request
fetch(request, parse: { json -> CurrentWeather? in
// Parse from JSON to CurrentWeather
if let currentWeatherDictionary = json["currently"] as? [String: AnyObject] {
return CurrentWeather(JSON: currentWeatherDictionary)
} else {
return nil
}
}, completion: completion)
}
}