Swift: assegnare un'istanza di un tipo generico a una variabile di classe?
Sto cercando di usare questo https://github.com/bricklife/JSONRPCKit. È una semplice implementazione di JSONRPC per Swift.
Questo è l'esempio nel file readme. Abbastanza semplice.
// 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)
Vorrei tenerne traccia batch
, anche se viene creato in funzione di una classe. In modo che un'altra funzione (quando ricevo una risposta dal mio server). Può accedere alla specifica richiesta / batch / batchelement ed eseguire qualsiasi funzione necessaria.
Non posso creare vars nella mia classe utilizzando nessuno dei tipi di JSONRPCKit.
Ottengo errori del compilatore sulla falsariga di:
Il protocollo "Batch" può essere utilizzato solo come vincolo generico perché ha requisiti di tipo Self o associati
Il riferimento al tipo generico "Batch1" richiede argomenti in <...>
Il valore del tipo di protocollo "Richiesta" non può essere conforme a "Richiesta"; solo i tipi struct / enum / class possono essere conformi ai protocolli
Ho provato a utilizzare il generico nelle funzioni in qualche modo per evitare i miei problemi, ma neanche questo ha aiutato.
func store_b<Batch: JSONRPCKit.Batch>(_ batch: Batch){
Maggiori informazioni: batch
è di tipo Batch1
, in questo modo:
public struct Batch1<Request: JSONRPCKit.Request>: Batch {
ed Batch
è un protocollo
public protocol Batch {
C'è un modo semplice per tenere traccia della mia batch
richiesta e ottenere risposte / come posso utilizzare correttamente questi generici ?
Risposte
Suddividi la firma del metodo di creazione e il 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
è una funzione generica che accetta un singolo parametro di qualsiasi tipo . Il vincolo <Request: JSONRPCKit.Request>
specifica che il tipo di parametro deve essere conforme al protocollo JSONRPCKit.Request
.
Batch1
è una struttura generica che deve definire due tipi interni, entrambi associati a un tipo arbitrario . Di nuovo, <Request: JSONRPCKit.Request>
specifica che quel tipo arbitrario deve essere conforme al protocollo JSONRPCKit.Request
.
Il tipo restituito Batch1<Request>
lega insieme questi due generici, dicendo che il tipo utilizzato per la Batch1
struttura restituita corrisponderà al tipo del request
parametro.
Quando chiami il metodo create, devi compilare il tipo generico con un tipo concreto, come nell'esempio
let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))
Ora il compilatore può passare attraverso tutte le definizioni e creare implementazioni concrete per quel tipo:
public func create(_ request: Subtract) -> Batch1<Subtract>
public struct Batch1<Subtract>: Batch {
public typealias Responses = Int
public typealias Results = Result<Int, JSONRPCError>
}
Questo utilizza il fatto che Subtract
definisce typealias Response = Int
. Nota che niente è più generico; questi sono tutti i tipi concreti. Non avresti problemi con il tentativo di memorizzare una proprietà di tipo Batch1<Subtract>
.
Questo è il motivo per cui non puoi archiviare facilmente il batch in una proprietà: Swift non ha idea di quali tipi inserire!
Un modo per aggirare questo è memorizzare invece una chiusura, questo può avvolgere il batch generico in modo che la classe non debba saperlo
// 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)
Questo metodo accetta una chiusura specifica che corrisponde al tipo generico e la avvolge in una chiusura che non dipende più dal generico. In questo modo ogni batch può condividere la stessa proprietà di chiusura.