Swift: ошибка при создании класса Wrapper для универсального класса, соответствующего универсальному протоколу - PullRequest
0 голосов
/ 27 июня 2019

Я хочу создать класс Wrapper для универсального класса, который соответствует универсальному протоколу, но по какой-то причине я не могу заставить его работать должным образом.

Идея заключается в использовании оболочки AnyNetworkRequest как Erased-Type вдоль приложения, так что нет необходимости определять универсальные типы, как в _NetworkRequest .

Я не вижу, что отсутствует / неправильно на AnyNetworkRequest .Если кто-то может указать мне, что отсутствует или неправильно, я был бы признателен.

// Protocol with associatedtypes

public protocol NetworkRequest {

    associatedtype RequestSerializationType: RequestSerializationProtocol
    associatedtype RequestResponseType: NetworkResponseProtocol

    var requestSerializer: RequestSerializationType { get }
    var requestResponse: RequestResponseType? { get }
}


// Generic Request

public class _NetworkRequest<RequestSerializationType: RequestSerializationProtocol, RequestResponseType: NetworkResponseProtocol>: NetworkRequest {

    fileprivate init() {}

    public lazy var requestSerializer: RequestSerializationType = { RequestSerializationType.init() }()
    public var requestResponse: RequestResponseType?
}

// Concrete Request

public class DataNetworkRequest: _NetworkRequest<ConcreteHTTPRequestSerializer, ConcreteDataNetworkResponse> {}

// Concrete Request

public class JSONDataNetworkRequest: _NetworkRequest<ConcreteJSONRequestSerializer, ConcreteJSONDataNetworkResponse> {}


// Type Erased Wrapper
// Cannot make this wrapper work
// Error 1: Type 'AnyNetworkRequest' does not conform to protocol 'NetworkRequest'
// Error 2: Reference to invalid associated type 'RequestSerializationType' of type 'AnyNetworkRequest'

public class AnyNetworkRequest : NetworkRequest { //E1

    private let request : _NetworkRequest<RequestSerializationType, RequestResponseType> //E2

    init<T: NetworkRequest>(_ networkRequest: T) where T.RequestSerializationType == RequestSerializationType, T.RequestResponseType == RequestResponseType {
        request = networkRequest
    }
}

РЕДАКТИРОВАНИЕ: 1-е ИЗМЕНЕНИЕ

// Protocol with associatedtypes

public protocol NetworkRequest {

    associatedtype RequestSerializationType: RequestSerializationProtocol
    associatedtype RequestResponseType: NetworkResponseProtocol

    var requestSerializer: RequestSerializationType { get }
    var requestResponse: RequestResponseType? { get }
}


// Generic Request

public class _NetworkRequest<RST: RequestSerializationProtocol, RRT: NetworkResponseProtocol>: NetworkRequest {

    public typealias RequestSerializationType = RST
    public typealias RequestResponseType = RRT


    fileprivate init() {}

    public lazy var requestSerializer: RequestSerializationType = { RequestSerializationType.init() }()
    public var requestResponse: RequestResponseType?
}

// Concrete Request

public class DataNetworkRequest: _NetworkRequest<ConcreteHTTPRequestSerializer, ConcreteDataNetworkResponse> {}

// Concrete Request

public class JSONDataNetworkRequest: _NetworkRequest<ConcreteJSONRequestSerializer, ConcreteJSONDataNetworkResponse> {}


// Type Erased Wrapper
// Cannot make this wrapper work
// Error 1: Type 'AnyNetworkRequest' does not conform to protocol 'NetworkRequest'
// Error 2: Reference to invalid associated type 'RequestSerializationType' of type 'AnyNetworkRequest'

public class AnyNetworkRequest : NetworkRequest { //E1

    /*  // E1 forces me to include typealiases
        public typealias RequestSerializationType = <#type#>
        public typealias RequestResponseType = <#type#>
    */

    private let request : _NetworkRequest<RequestSerializationType, RequestResponseType>

    var requestSerializer: RequestSerializationType { //E2
        return request.requestSerializer
    }

    var requestResponse: RequestResponseType? {
        return request.requestResponse
    }

    init<T: NetworkRequest>(_ networkRequest: T) where T.RST == RequestSerializationType, T.RRT == RequestResponseType {
        request = networkRequest
    }
}

Ответы [ 2 ]

1 голос
/ 27 июня 2019

Это не совсем то, как работают ластики типов - роль ластиков типов заключается в том, чтобы разрешить однородные интерфейсы в гетерогенном мире, где многие типы могут соответствовать интересующим протоколам.

Взять, к примеру, AnySequence, его интерфейс аналогичен следующему:

struct AnySequence<Element>: Sequence {
    init<S>(_ sequence: S) where S == Sequence, S.Element == Element
}

AnySequence стирает исходный тип последовательности, а не тип Element.В вашем случае вы не можете избавиться от двух связанных типов, ластик типов может скрывать только фактический класс, соответствующий NetworkRequest.AnyRequest все равно потребуется информация о двух типах.

1 голос
/ 27 июня 2019

Ошибки довольно просто объяснить.

1) Ваш класс AnyNetworkRequest действительно не соответствует протоколу NetworkRequest. И я не понимаю, почему это должно быть, кстати. Неудовлетворенными требованиями являются свойства requestSerializer и requestResponse вместе с необходимыми псевдонимами типов. В общем, вы можете переписать это следующим образом:

public class AnyNetworkRequest: NetworkRequest {

    var requestSerializer: RequestSerializationType { 
       return request.requestSerializer
    }

    var requestResponse: RequestResponseType? { 
       return request.requestResponse
    }

    private let request : _NetworkRequest<RequestSerializationType, RequestResponseType>

   // ...
}

НО это приведет нас к

2) Где вам нужно указать что-то для связанных типов. Вы не можете использовать RequestSerializationType и RequestResponseType в обобщенном объявлении, поскольку они не являются конкретными типами.

Таким образом, вы не можете выполнить стирание типа таким способом.

Я не знаю, почему здесь нужно избавляться от обобщений, кроме ввода меньшего количества букв, но я могу предложить использовать псевдонимы типа:

typealias DataNetworkRequest = _NetworkRequest<ConcreteHTTPRequestSerializer, ConcreteDataNetworkResponse>

typealias JSONDataNetworkRequest = _NetworkRequest<ConcreteJSONRequestSerializer, ConcreteJSONDataNetworkResponse>

Таким образом, вы избежите избыточного наследования и будете иметь более четко выраженные типы на случай, если это ваша цель.

AnyNetworkRequest в этом случае вообще не понадобится.

...