Swift: назначить экземпляр универсального типа переменной класса?
Я пытаюсь использовать это https://github.com/bricklife/JSONRPCKit. Это простая реализация JSONRPC для Swift.
Это пример в readme. Довольно просто.
// 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)
Я бы хотел отслеживать batch
, даже если он создан в функции класса. Так что другая функция (когда я получаю ответ от моего сервера). Может получить доступ к конкретному запросу / пакету / пакетному элементу и выполнить любую функцию, которая ему нужна.
Я не могу создавать вары в своем классе, используя любой из типов из JSONRPCKit.
Я получаю ошибки компилятора в следующих строках:
Протокол "Пакетный" можно использовать только как общее ограничение, поскольку он имеет требования типа Self или связанные с ним.
Ссылка на универсальный тип Batch1 требует аргументов в <...>
Значение типа протокола «Запрос» не может соответствовать «Запросу»; только типы struct / enum / class могут соответствовать протоколам
Я пытался как-то использовать универсальные функции в функциях, чтобы избежать проблем, но это тоже не помогло.
func store_b<Batch: JSONRPCKit.Batch>(_ batch: Batch){
Дополнительная информация: batch
имеет тип Batch1
, например:
public struct Batch1<Request: JSONRPCKit.Request>: Batch {
и Batch
это протокол
public protocol Batch {
Есть ли простой способ отслеживать мой batch
запрос и получать ответы / как мне правильно использовать эти дженерики ?
Ответы
Разбейте сигнатуру метода создания и тип 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
- это универсальная функция, которая принимает один параметр любого типа . Ограничение <Request: JSONRPCKit.Request>
указывает, что тип параметра должен соответствовать протоколу JSONRPCKit.Request
.
Batch1
- это универсальная структура, которая должна определять два внутренних типа, каждый из которых связан с некоторым произвольным типом . Опять же, <Request: JSONRPCKit.Request>
указывает, что этот произвольный тип должен соответствовать протоколу JSONRPCKit.Request
.
Тип возвращаемого значения Batch1<Request>
связывает эти два обобщения вместе, говоря, что тип, используемый для возвращаемой Batch1
структуры, будет соответствовать типу request
параметра.
Когда вы вызываете метод create, вы должны заполнить универсальный тип конкретным типом, как в примере
let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))
Теперь компилятор может просмотреть все определения и создать конкретные реализации для этого типа:
public func create(_ request: Subtract) -> Batch1<Subtract>
public struct Batch1<Subtract>: Batch {
public typealias Responses = Int
public typealias Results = Result<Int, JSONRPCError>
}
Здесь используется тот факт, что Subtract
определяет typealias Response = Int
. Обратите внимание, что больше нет ничего общего; это все конкретные типы. У вас не возникнет проблем с попыткой сохранить свойство типа Batch1<Subtract>
.
Вот почему вы не можете легко сохранить пакет в свойстве: Swift не знает, какие типы в него помещать!
Один из способов обойти это - вместо этого сохранить закрытие, это может обернуть общий пакет, так что класс не должен знать об этом
// 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)
Этот метод принимает конкретное закрытие, которое соответствует универсальному типу, и оборачивает его в закрытие, которое больше не зависит от универсального типа. Таким образом, каждая партия может иметь одно и то же свойство закрытия.