I have a protocol for simple http client:
protocol HttpClientProtocol {
func request(route: any RouteProtocol)
}
protocol RouteProtocol {
associatedtype T : Encodable //this is probably irrelevant, but nevertheless
// ...stuff
}
Now, I'm trying to use this protocol as a type that will be used by clients (e.g. some MyThingRepository
will inject HttpClientProtocol
without knowing the specific type that will be injected - for loose coupling). Then I'm trying to create a concrete class from that protocol.
class ApiClient : HttpClientProtocol {
func request(route: any ApiRouteProtocol) {
//... use specific ApiRouteProtocol here, not the general RouteProtocol
}
}
protocol ApiRouteProtocol : RouteProtocol, URLRequestConvertible { }
Problem is that I get Type 'ApiClient' does not conform to protocol 'HttpClientProtocol'
compilation error.
My intuition is that if the protocol allows any RouteProtocol
, then the class implementing it should be able to narrow down the type to ApiRouteProtocol
. However, the compiler doesn't agree. Is my intuition wrong or is it a Swift limitation?
What could I do to work around it? My only working idea for now is to force cast the route
to ApiRouteProtocol
in ApiClient
.
CodePudding user response:
Update
For external access from another module where you can't use an extension I believe the only solution is to declare the functions as it is defined in the protocol and then internally check for the correct conformance
class ApiClient : HttpClientProtocol {
func request(route: any RouteProtocol) {
guard let route = route as? (any ApiRouteProtocol) else {
return // or some error handling
}
// do stuff
}
}
Old solution
One solution is to conform to the protocol but make use of polymorphism to handle calls using ApiRouteProtocol
. Not a perfect solution but it works since for any route object that conforms to ApiRouteProtocol
the function in the extension will be called.
class ApiClient : HttpClientProtocol {
func request(route: any RouteProtocol) {
return // or whatever you want
}
}
extension ApiClient {
func request(route: any ApiRouteProtocol) {
// do stuff
}
}
CodePudding user response:
I overcomplicated things by introducing ApiRouteProtocol
. I imagined I needed it to conform to UrlRequestConvertible
in its extension, but the thing is that I can do a private extension to the RouteProtocol
itself and
handle the specific implementation required by ApiClient : HttpClientProtocol
there.
class ApiClient : HttpClientProtocol {
func request(route: any RouteProtocol) {
//...route.asURLRequest()...
}
}
extension RouteProtocol {
func asURLRequest() throws -> URLRequest { //...
}
And now my repository only knows about abstract HttpClientProtocol
and RouteProtocol
and has no interest in ApiClient and its internals which is what I was trying to achieve here.