1

I am currently working my way through he Treehouse IOS Swift course, and we are building a weather app. I've gotten to a point where I keep getting an error that my class isn't conforming to my protocol, but I can't figure out why.

Here is my protocol declaration:

public protocol APIClient {

    var configuration: URLSessionConfiguration { get }
    var session: URLSession { get }

    func JSONTaskWithRequest(request: URLRequest, completion: JSONTaskCompletion) -> JSONTask
    func fetch<T: JSONDecodable>(request: URLRequest, parse: (JSON) -> T?, completion: (APIResult<T>) -> Void)

}

And then I have a protocol extension where I have default implementations of my two methods declared in the protocol, and one of the methods is calling the other method, as you can see below.

public extension APIClient {

func JSONTaskWithRequest(request: URLRequest, completion: @escaping JSONTaskCompletion) -> JSONTask {
        let task = session.dataTask(with: request) { data, response, error in

            guard let HTTPResponse = response as? HTTPURLResponse else {

                let userInfo = [
                    NSLocalizedDescriptionKey: NSLocalizedString("Missing HTTP Response", comment: "")
                ]
                let error = NSError(domain: BPSnetworkingErrorDomain, code: MissingHTTPReponseError, userInfo: userInfo)
                completion(nil, response as! HTTPURLResponse, error)
                return
            }

            if data == nil  {
                if let error = error {
                    completion(nil, response as! HTTPURLResponse, error as NSError?)
                }

            } else {
                switch HTTPResponse.statusCode {
                case 200:
                    do {
                        let JSON = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: AnyObject]
                        completion(JSON, HTTPResponse, nil)
                    } catch let error as NSError {
                        completion(nil, HTTPResponse, error)
                    }
                default: print("Received HTTP Response \(HTTPResponse.statusCode) - not handled")
                }
            }
        }
        return task
    }

    public func fetch<T>(request: URLRequest, parse: @escaping (JSON) -> T?, completion: @escaping (APIResult<T>) -> Void) {
        let task = JSONTaskWithRequest(request: request) { json, response, error in
            DispatchQueue.main.async {
            guard let json = json else {
                if let error = error {
                    completion(.Failure(error))
                } else {
                    let error = "Something is really wrong.  There was no JSON object created, but there was no error either."
                    completion(.Failure(error as! Error))
                }
                return
            }

            if let value = parse(json) {
                completion(.Success(value))
            } else {
                let error = NSError(domain: BPSnetworkingErrorDomain, code: unexpectedResponseError, userInfo: nil)
                completion(.Failure(error))
            }
        }
        }

        task.resume()
    }
}

Then I have my class declaration where I am getting my non conformity error.

final class ForecastAPIClient: APIClient {

    let configuration: URLSessionConfiguration
    lazy var session: URLSession = {
    return URLSession(configuration: self.configuration)
}()

    private let token: String

    init(config: URLSessionConfiguration, APIKey: String) {
        self.configuration = config
        self.token = APIKey
    }

    convenience init(APIKey: String) {
        self.init(config: URLSessionConfiguration.default, APIKey: APIKey)
    }

    func fetchCurrentWeather(coordinate: Coordinate, completion: @escaping (APIResult<CurrentWeather>) -> Void) {
        let request = Forecast.Current(token: self.token, coordinate: coordinate).request



        fetch(request: request, parse: { (JSON) -> CurrentWeather? in

            if let currentWeatherDictionary = JSON["currently"] as? [String: AnyObject] {
                return CurrentWeather(JSON: currentWeatherDictionary)
            } else {
                return nil
            }
            }, completion: completion)

    }



}

I've done a lot of reading around for several hours trying to figure out what is going on here. From what I understand, I shouldn't need to define those two methods in my class since they have default implementations in the protocol extension. I came across the issue of public/internal types and things like that, that someone else was having here on StackExchange with their extensions, (as you an see by my labeling things public and what not), but that didn't seem to help in my case. The only way I've been able to get the error to go away, is by commenting out those method declarations in the original protocol declaration. Which seems to indicate to me that either the class, or the protocol, or something isn't seeing the extension for some reason, however, if I command click on the fetch method call in the class declaration, it takes me to the definition of it in the extension. I haven't been able to find a solution, or even someone who is doing this similar thing, there are several people on Treehouse that seem to be having this same issue as well.

Also, I download the teachers code, and converted it to Swift 3, and it was getting the same error as well, so maybe it's an issues with having a different version of Xcode the what he used when he made the video?

I feel like I'm kind of grasping at straws a little bit, but I really am eager to get this figured out, so any possible help would be so much appreciated.

Thank you!

bshock84
  • 177
  • 1
  • 8
  • 1
    The "Report navigator" in Xcode has the full compiler log, and that should contain more detailed information about why the compiler thinks that your class does not conform to the protocol. – Martin R Oct 07 '16 at 07:08
  • Thank you, this helped me figure out what was going on. Apparently if you mark a closure as @escaping in the extension, it also has to be marked that way in the protocol declaration, makes sense after thinking about it, but I am still new to that concept. – bshock84 Oct 07 '16 at 09:29

1 Answers1

1

I used Xcode's playground to test and play around with your code. I took your code (protocol declaration, protocol extension, and class declaration) and heavily simplified the JSONTaskWithRequest() and fetch() functions. The code compiled with no "non conformity error." Here is the code I used:

//: Playground :    
import UIKit

// protocol declaration
public protocol APIClient {

    var configuration: URLSessionConfiguration { get }
    var session: URLSession { get }

    func JSONTaskWithRequest()
    func fetch()
}

// protocol extension
public extension APIClient {
    func JSONTaskWithRequest() {
        print("JSONTaskWithRequest here")
    }
    func fetch() {
        print("fetch here")
    }
}

// class declaration
final class ForecastAPIClient: APIClient {
    let configuration: URLSessionConfiguration
    lazy var session: URLSession = {
        return URLSession(configuration: self.configuration)
    }()

    private let token: String

    init(config: URLSessionConfiguration, APIKey: String) {
        self.configuration = config
        self.token = APIKey
    }
}

I suspect that there is a bug in JSONTaskWithRequest and/or fetch. I suggest you isolate either function to figure out which one is giving you the error. Then debug from there.

Also, just another suspicion. In the extension's JSONTaskWithRequest function implementation, you have:

let task = session.dataTask(with: request) {...}
return task

JSONTaskWithRequest is required to return a JSONTask. Maybe you need to downcast task:

return task as! JSONTask

I couldn't use your provided code because things like JSONTaskCompletion and JSONDecodable aren't recognized by Swift. Are you using a third party JSON swift library?

Zion
  • 1,562
  • 13
  • 32
  • 1
    Okay, I will play around with that a bit and see if I can find something in there. There is some code that I left out to keep the question from being a mile long. JSONTaskCompletion, JSONTask, and JSON are all typealiases, (the JSONTask type is actually a URLSessionDataTask, so returning task is the same as returning a JSONTask). JSONDecodable is another protocol defining an optional dictionary for the JSON data. Thank you for the reply! – bshock84 Oct 07 '16 at 08:52
  • 1
    Okay! I Figured it out! So apparently if you mark a closure as `@escaping` in the extension, it also has to be marked that way in the protocol declaration. Can't believe something so trivial has caused me to spend so much time on it, ha. It makes sense I guess, but I am still newish to the escaping/non escaping thing. – bshock84 Oct 07 '16 at 09:26