Swift: ¿Asignar una instancia de un tipo genérico a una variable de clase?
Estoy tratando de usar esto https://github.com/bricklife/JSONRPCKit. Es una implementación simple de JSONRPC para Swift.
Este es el ejemplo del archivo Léame. Bastante sencillo.
// Generating request JSON
let batchFactory = BatchFactory(version: "2.0", idGenerator: NumberIdGenerator())
let request = Subtract(minuend: 42, subtrahend: 23)
let batch = batchFactory.create(request)
batch.requestObject // ["jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1]
// Parsing response JSON
let responseObject: Any = ["jsonrpc": "2.0", "result": 19, "id": 1]
let response = try! batch.responses(from: responseObject)
Me gustaría realizar un seguimiento batch
, incluso si se crea en función de una clase. Entonces, otra función (cuando obtengo una respuesta de mi servidor). Puede acceder a la solicitud / lote / elemento de lote específico y ejecutar cualquier función que necesite.
No puedo crear vars en mi clase usando ninguno de los tipos de JSONRPCKit.
Recibo errores del compilador en la línea de:
El protocolo 'Lote' solo se puede usar como una restricción genérica porque tiene requisitos de tipo Self o asociados
La referencia al tipo genérico 'Batch1' requiere argumentos en <...>
El valor del tipo de protocolo 'Solicitud' no puede ajustarse a 'Solicitud'; solo los tipos de estructura / enumeración / clase pueden ajustarse a los protocolos
Intenté usar el genérico en funciones de alguna manera para evitar mis problemas, pero eso tampoco ayudó.
func store_b<Batch: JSONRPCKit.Batch>(_ batch: Batch){
Más información: batch
es de tipo Batch1
, así:
public struct Batch1<Request: JSONRPCKit.Request>: Batch {
y Batch
es un protocolo
public protocol Batch {
¿Existe una forma sencilla de realizar un seguimiento de mi batch
solicitud y obtener respuestas / cómo uso estos genéricos correctamente?
Respuestas
Desglose la firma del método de creación y el tipo Batch1
public func create<Request: JSONRPCKit.Request>(_ request: Request) -> Batch1<Request>
public struct Batch1<Request: JSONRPCKit.Request>: Batch {
public typealias Responses = Request.Response
public typealias Results = Result<Request.Response, JSONRPCError>
}
create
es una función genérica que toma un solo parámetro de cualquier tipo . La restricción <Request: JSONRPCKit.Request>
especifica que el tipo de parámetro debe ajustarse al protocolo JSONRPCKit.Request
.
Batch1
es una estructura genérica que necesita definir dos tipos internos, los cuales están asociados con algún tipo arbitrario . Nuevamente, <Request: JSONRPCKit.Request>
especifica que ese tipo arbitrario debe ajustarse al protocolo JSONRPCKit.Request
.
El tipo de retorno Batch1<Request>
une estos dos genéricos, diciendo que el tipo utilizado para la Batch1
estructura devuelta coincidirá con el tipo del request
parámetro.
Cuando llama al método de creación, debe completar el tipo genérico con un tipo concreto, como en el ejemplo
let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))
Ahora el compilador puede revisar todas las definiciones y crear implementaciones concretas para ese tipo:
public func create(_ request: Subtract) -> Batch1<Subtract>
public struct Batch1<Subtract>: Batch {
public typealias Responses = Int
public typealias Results = Result<Int, JSONRPCError>
}
Esto usa el hecho que Subtract
define typealias Response = Int
. Tenga en cuenta que ya nada es genérico; todos estos son tipos concretos. No tendría ningún problema en intentar almacenar una propiedad de tipo Batch1<Subtract>
.
Es por eso que no puede almacenar fácilmente el lote en una propiedad: ¡Swift no tiene idea de qué tipos poner en él!
Una forma de evitar esto es almacenar un cierre, esto puede envolver el lote genérico de manera que la clase no tenga que saberlo
// closure property
var responseProcessor: ((Any) -> Void)?
func createBatch<R: JSONRPCKit.Request>(request: R, processor: @escaping (R.Response) -> Void) {
let batch = batchFactory.create(request)
self.responseProcessor = { responseObject in
let response = try! batch.responses(from: responseObject)
processor(response)
}
}
// somewhere else when you get the responseObject
responseProcessor?(responseObject)
Este método toma un cierre específico que coincide con el tipo genérico y lo envuelve en un cierre que ya no depende del genérico. De esta forma, cada lote puede compartir la misma propiedad de cierre.