Search code examples
iosswiftgenericstype-conversionswift4

Swift generics issue: generic parameter could not be inferred


With this code:

public protocol LoginInfoBase: Decodable {
    var access_token: String? { get }
    var notifications: [PushNotification] { get }
}

public class ExampleLoginInfo: LoginInfoBase {
    public var access_token: String? = nil
    public var notifications: [PushNotification] = []
    
    public var user_info: UserInfo? = nil
    public var more_example_data: String? = nil
}

public func genericQuery<T: Decodable>(urlString: String, method: QueryType, params: Parameters?, decodable: T.Type,completionHandler: @escaping (Swift.Result<T, Error>) -> Void) {
       <more code here>
    }
}

public func getLoginInfo(loginInfoClass: LoginInfoBase.Type, completionHandler: @escaping (Swift.Result<LoginInfoBase, Error>) -> Void) {
    genericQuery(urlString: "\(baseURL)/users/get_login_info/", method: .get, params: nil, decodable: loginInfoClass.self) { result in
        <more code here>
    }
}

I then call getLoginInfo...

getLoginInfo(loginInfoClass: ExampleLoginInfo.self) { result in 
    <more code here>
}

...and get this error:

Generic parameter 'T' could not be inferred
Cannot convert value of type 'LoginInfoBase.Type' to expected argument type 'T.Type'

Here's the background. I'm trying to set up a generic login library that can be invoked with a class that contains user data that's specific to each app that it's used in. In this app I subclass LoginInfoBase as ExampleLoginInfo to add the extra user data. My intention is that library will then decode the user data returned from the server into ExampleLoginInfo.

Any ideas how to fix the error?


Solution

  • This is the wrong signature:

    public func getLoginInfo(loginInfoClass: LoginInfoBase.Type, completionHandler: @escaping (Swift.Result<LoginInfoBase, Error>) -> Void)
    

    You mean this:

    public func getLoginInfo<T: LoginInfoBase>(loginInfoClass: T.Type, completionHandler: @escaping (Swift.Result<LoginInfoBase, Error>) -> Void)
                            ^^^^^^^^^^^^^^^^^^                 ^
    

    You need to pass a concrete type to getLoginInfo that conforms to LoginInfoBase. Not just any subtype. This matches your genericQuery method.

    You should then modify your call to genericQuery as:

    genericQuery(urlString: "\(baseURL)/users/get_login_info/",
                 method: .get,
                 params: nil,
                 decodable: T.self) { ... } // use T.self here.
    

    For more details, see Alexander's link.