Swift: attribuer une instance d'un type générique à une variable de classe?

Aug 20 2020

J'essaye d'utiliser ceci https://github.com/bricklife/JSONRPCKit. Il s'agit d'une implémentation simple de JSONRPC pour Swift.

C'est l'exemple du fichier readme. Assez facile.

// 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)

Je voudrais garder une trace de batch, même s'il est créé dans une fonction d'une classe. Alors qu'une autre fonction (quand je reçois une réponse de mon serveur). Peut accéder à la demande / lot / élément de lot spécifique et exécuter la fonction dont il a besoin.

Je ne peux pas créer de variables dans ma classe en utilisant l'un des types de JSONRPCKit.

J'obtiens des erreurs de compilation du type:

Le protocole 'Batch' ne peut être utilisé que comme contrainte générique car il a des exigences de type Self ou associé

La référence au type générique 'Batch1' nécessite des arguments dans <...>

La valeur du type de protocole «Request» ne peut pas être conforme à «Request»; seuls les types struct / enum / class peuvent se conformer aux protocoles

J'ai essayé d'utiliser le générique dans les fonctions d'une manière ou d'une autre pour éviter mes problèmes, mais cela n'a pas aidé non plus.

    func store_b<Batch: JSONRPCKit.Batch>(_ batch: Batch){

Plus d'infos: batchest de type Batch1, comme ceci:

public struct Batch1<Request: JSONRPCKit.Request>: Batch {

et Batchest un protocole

public protocol Batch {

Existe-t-il un moyen simple de suivre ma batchdemande et d'obtenir des réponses / comment utiliser correctement ces génériques ?

Réponses

Max Aug 20 2020 at 03:16

Décomposer la signature de la méthode de création et le type 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>
}

createest une fonction générique qui prend un seul paramètre de n'importe quel type . La contrainte <Request: JSONRPCKit.Request>spécifie que le type du paramètre doit être conforme au protocole JSONRPCKit.Request.

Batch1est une structure générique qui doit définir deux types internes, tous deux associés à un type arbitraire . Encore une fois, <Request: JSONRPCKit.Request>spécifie que ce type arbitraire doit être conforme au protocole JSONRPCKit.Request.

Le type de retour Batch1<Request>lie ces deux génériques ensemble, en disant que le type utilisé pour la Batch1structure retournée correspondra au type du requestparamètre.

Lorsque vous appelez la méthode create, vous devez remplir le type générique avec un type concret, comme dans l'exemple

let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))

Maintenant, le compilateur peut parcourir toutes les définitions et créer des implémentations concrètes pour ce type:

public func create(_ request: Subtract) -> Batch1<Subtract>

public struct Batch1<Subtract>: Batch {
    public typealias Responses = Int
    public typealias Results = Result<Int, JSONRPCError>
}

Cela utilise le fait qui Subtractdéfinit typealias Response = Int. Notez que rien n'est plus générique; ce sont tous des types concrets. Vous n'auriez aucun problème à essayer de stocker une propriété de type Batch1<Subtract>.


C'est pourquoi vous ne pouvez pas facilement stocker le lot dans une propriété: Swift n'a aucune idée des types à y mettre!

Une façon de contourner cela consiste à stocker une fermeture à la place, cela peut envelopper le lot générique de telle sorte que la classe n'ait pas à le savoir

// 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)

Cette méthode prend une fermeture spécifique qui correspond au type générique et l'encapsule dans une fermeture qui ne dépend plus du générique. De cette façon, chaque lot peut partager la même propriété de fermeture.