无法将类型X的值转换为预期参数类型XX

时间:2018-11-08 12:57:54

标签: ios swift generics alamofire swifty-json

我正在尝试使用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 :(得分:0)

您要传递的.successResponse必须覆盖/实现RESPONSE_TYPE,如在BaseViewController的typealias RES_TYPE中声明的那样。

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())
    }

}

在这种情况下,您应该尝试在BaseViewController中使用EVNetworkingObject切换RESPONSE_TYPE。