Класс не соответствует протоколу с расширением, содержащим реализацию по умолчанию
В настоящее время я прорабатываю курс Treehouse IOS Swift, и мы создаем приложение для погоды. Я дошел до того, что постоянно получаю сообщение об ошибке, что мой класс не соответствует моему протоколу, но я не могу понять, почему.
Вот моя декларация протокола:
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)
}
И затем у меня есть расширение протокола, где у меня есть реализации по умолчанию двух моих методов, объявленных в протоколе, и один из методов вызывает другой метод, как вы можете видеть ниже.
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()
}
}
Затем у меня есть объявление класса, где я получаю ошибку несоответствия.
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)
}
}
В течение нескольких часов я много читал, пытаясь понять, что здесь происходит. Из того, что я понимаю, мне не нужно определять эти два метода в моем классе, так как они имеют реализации по умолчанию в расширении протокола. Я сталкивался с проблемой публичных / внутренних типов и тому подобного, которые кто-то еще имел здесь на StackExchange со своими расширениями (как вы видите из-за того, что я маркировал вещи как публичные, а что нет), но это, похоже, не помогло в моем случае. Единственный способ устранить ошибку - это закомментировать эти объявления методов в исходном объявлении протокола. Что, по-видимому, указывает мне, что либо класс, либо протокол, либо что-то не видят расширения по какой-то причине, однако, если я нажму на команду fetch
вызов метода в объявлении класса, он приводит меня к определению его в расширении. Я не смог найти решение, или даже кто-то, кто делает подобную вещь, есть несколько человек на Treehouse, которые, кажется, тоже имеют эту проблему.
Кроме того, я загружаю код учителя и конвертирую его в Swift 3, и он тоже получал ту же ошибку, так что, возможно, это проблемы с другой версией Xcode, чем он использовал, когда делал видео?
Мне кажется, что я немного цепляюсь за соломинку, но я действительно хочу выяснить это, так что любая возможная помощь будет очень цениться.
Спасибо!
1 ответ
Я использовал игровую площадку Xcode, чтобы проверить и поиграть с вашим кодом. Я взял ваш код (объявление протокола, расширение протокола и объявление класса) и сильно упростил JSONTaskWithRequest()
а также fetch()
функции. Код скомпилирован без "ошибки несоответствия". Вот код, который я использовал:
//: 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
}
}
Я подозреваю, что есть ошибка в JSONTaskWithRequest и / или выборке. Я предлагаю вам выделить любую функцию, чтобы выяснить, какая из них дает вам ошибку. Тогда отлаживайте оттуда.
Также просто еще одно подозрение. В реализации функции расширения JSONTaskWithRequest у вас есть:
let task = session.dataTask(with: request) {...}
return task
JSONTaskWithRequest требуется для возврата JSONTask. Может быть, вам нужно убить task
:
return task as! JSONTask
Я не смог использовать предоставленный вами код, потому что такие вещи, как JSONTaskCompletion и JSONDecodable, не распознаются Swift. Вы используете стороннюю библиотеку JSON swift?