Lo estás abordando de manera incorrecta: en Swift, a diferencia de Objective-C, las clases tienen tipos específicos e incluso tienen una jerarquía de herencia (es decir, si la clase Bhereda de A, entonces B.Typetambién hereda de A.Type):
class A {}
class B: A {}
class C {}
// B inherits from A
let object: A = B()
// B.Type also inherits from A.Type
let type: A.Type = B.self
// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self
Es por eso que no debes usar AnyClass, a menos que realmente quieras permitir cualquier clase. En este caso, el tipo correcto sería T.Type, porque expresa el vínculo entre el returningClassparámetro y el parámetro del cierre.
De hecho, usarlo en lugar de AnyClasspermite al compilador inferir correctamente los tipos en la llamada al método:
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
// The compiler correctly infers that T is the class of the instances of returningClass
handler(returningClass())
}
Ahora está el problema de construir una instancia Tpara pasar handler: si intenta ejecutar el código en este momento, el compilador se quejará de que Tno es constructible (). Y con razón: Ttiene que estar explícitamente restringido para requerir que implemente un inicializador específico.
Esto se puede hacer con un protocolo como el siguiente:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Entonces solo tiene que cambiar las restricciones genéricas de invokeServicefrom <T>a <T: Initable>.
Propina
Si obtiene errores extraños como "No se puede convertir el tipo de expresión '()' a tipo 'Cadena'", a menudo es útil mover cada argumento de la llamada al método a su propia variable. Ayuda a reducir el código que está causando el error y descubrir problemas de inferencia de tipos:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Ahora hay dos posibilidades: el error se mueve a una de las variables (lo que significa que la parte incorrecta está allí) o aparece un mensaje críptico como "No se puede convertir el tipo de expresión ()en tipo ($T6) -> ($T6) -> $T5".
La causa del último error es que el compilador no puede inferir los tipos de lo que escribió. En este caso, el problema es que Tsolo se usa en el parámetro del cierre y el cierre que pasó no indica ningún tipo en particular, por lo que el compilador no sabe qué tipo inferir. Al cambiar el tipo de returningClassincluir, Tle da al compilador una forma de determinar el parámetro genérico.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }