Невозможно преобразовать значение типа X в ожидаемый тип аргумента XX - PullRequest
0 голосов
/ 08 ноября 2018

Я пытаюсь написать сетевой менеджер, используя Generics. И я не могу этого сделать. Ниже приведен мой код и скриншот ошибки. Здесь BaseViewController реализует NetworkManagerListener. Я думаю, что я сделал код более сложным, поэтому, пожалуйста, помогите мне понять, что я делаю неправильно и как избавиться от этой ошибки

import Alamofire
import EVReflection
import Alamofire_SwiftyJSON
import SwiftyJSON

protocol NetworkManagerListener {
    associatedtype RES_TYPE
    func successResponse(statusCode: Int, response: RES_TYPE?)
    func errorResponse(statusCode: Int, error: ErrorResponse)
}

class NetworkingManager<RES: EVNetworkingObject, ER: ErrorResponse, T: Any> {

    static func registerUser<BVC: BaseViewController<T>>(baseViewController: BVC, registerUserRequest: RegisterUserRequest, checkTokenExpiry: Bool) {
        handleRequest(baseViewController: baseViewController, url: API.REGISTER_USER, requestModel: registerUserRequest, checkTokenExpiry: checkTokenExpiry)
    }

    private static func handleRequest<BVC: BaseViewController<T>, REQ: EVNetworkingObject>(baseViewController: BVC, url: String, requestModel: REQ, checkTokenExpiry: Bool) {
        if let isReachable = NetworkReachabilityManager()?.isReachable, isReachable {
            if let userModelDictionary = requestModel.toDictionary() as? Dictionary<String, Any> {
                var headersDictionary: HTTPHeaders?
                if let user = DatabaseManager.getUser() {
                    headersDictionary = [String: String]()
                    headersDictionary?["Authorization"] = "Bearer \(user.userToken)"
                    headersDictionary?["Content-Type"] = "application/json"
                    print(headersDictionary as Any)
                }

                Alamofire.request(url, method: .post, parameters: userModelDictionary, encoding: JSONEncoding.default, headers: headersDictionary ?? nil).validate(statusCode: 200..<300).responseSwiftyJSON { response in
                    if let statusCode = response.response?.statusCode {
                        do {
                            switch response.result {
                            case .failure(let error):
                                var errorResponse: ER = ER(message: "Please try again later")
                                if let data = try response.result.value?.rawData(), !data.isEmpty {
                                    errorResponse = ER(data: data)
                                }
                                if checkTokenExpiry || statusCode >= STATUS_CODE.SERVER_ERROR_RANGE_STARTING {
                                    handleErrorGlobally(baseViewController: baseViewController, statusCode: statusCode, errorResponse: errorResponse)
                                } else {
                                    handleError(baseViewController: baseViewController, statusCode: statusCode, errorResponse: errorResponse)
                                }
                                break
                            case .success:
                                if let data = try response.result.value?.rawData(), let type = response.result.value?.type {
                                    print("data: \(data)")
                                    switch type {
                                    case .array:
                                        baseViewController.successResponse(statusCode: statusCode, response: [RES](data: data))
                                        break
                                    case .dictionary:
                                        baseViewController.successResponse(statusCode: statusCode, response: RES.init(data: data))
                                        break
                                    default:
                                        print("unhandled type: \(type)")
                                        break
                                    }
                                } else {
                                    baseViewController.successResponse(statusCode: statusCode, response: nil as RES?)
                                }
                                break
                            }
                        } catch(let error) {
                            print("error: \(error)")
                        }
                    }
                }
            }
        } else {
            baseViewController.networkFailure()
        }
    }

    private static func handleErrorGlobally<BVC: BaseViewController<T>, ER: ErrorResponse>(baseViewController: BVC, statusCode: Int, errorResponse: ER) {
        if statusCode == STATUS_CODE.UN_AUTHORIZED {
            baseViewController.tokenExpired()
        } else if statusCode >= STATUS_CODE.SERVER_ERROR_RANGE_STARTING {
            baseViewController.serverError()
        } else {
            baseViewController.networkError(statusCode, errorResponse)
        }
    }

    private static func handleError<BVC: BaseViewController<T>, ER: ErrorResponse>(baseViewController: BVC, statusCode: Int, errorResponse: ER) {
        baseViewController.errorResponse(statusCode: statusCode, error: errorResponse)
    }
}

BaseViewController:

import UIKit
import EVReflection

class BaseViewController<RESPONSE_TYPE>: UIViewController, NetworkManagerListener {

    typealias RES_TYPE = RESPONSE_TYPE
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func tokenExpired() {
        //TODO: Need to create an alert dialog showing the user to re-login
        print("Token expired")
    }

    func networkError(_ statusCode: Int, _ errorResponse: ErrorResponse) {
        //general error with unhandled status code
        let refreshAlert = UIAlertController(title: nil, message: errorResponse.message, preferredStyle: UIAlertController.Style.alert)
        refreshAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
        self.present(refreshAlert, animated: true, completion: nil)
    }

    func networkFailure() {
        //handling no internet connection
        let alert = UIAlertController(title: "Notice", message: "Please check your internet connection", preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }

    func serverError() {
        //TODO: Need to handle when error came from server
        let alert = UIAlertController(title: "Notice", message: "Please try again later", preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }

    func successResponse(statusCode: Int, response: RESPONSE_TYPE?) {
        print("unhandled response is \(String(describing: response))")
    }

    func errorResponse(statusCode: Int, error: ErrorResponse) {
        let alert = UIAlertController(title: nil, message: error.message, preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}

Пример ответа:

import EVReflection

class RegisterUserResponse: EVNetworkingObject {
    var userName: String?
    var email: String?
    var firstName: String?
    var lastName: String?

    override func propertyMapping() -> [(keyInObject: String?, keyInResource: String?)] {
        return [("userName", "username"), ("firstName", "first_name"), ("lastName", "last_name")]
    }
}

enter image description here

1 Ответ

0 голосов
/ 08 ноября 2018

То, что вы пытаетесь передать в .successResponse, должно переопределить / реализовать RESPONSE_TYPE, как объявлено в typealias RES_TYPE в BaseViewController.

class SomeListener: NetworkManagerListener {
    typealias RES_TYPE = NSString

    func successResponse(statusCode: Int, response: SomeListener.RES_TYPE?) {

    }
    func errorResponse(statusCode: Int, error: Int) {

    }
}

class Res: NSString {

}

class Er {

}

class NetworkingManager<RES: Res, ER: Er, T: Any> {

    func someCall() {
        let a = SomeListener()
        a.successResponse(statusCode: 1, response: Res())
    }

}

В вашем случае вы должны попытаться переключить RESPONSE_TYPE с EVNetworkingObject в вашем BaseViewController.

...