I usually take a somewhat different approach here and try to always deal with concrete types in these scenarios. So I'd keep the original protocols:
public protocol ServerResponse {
}
public protocol CacheableResponse {
var cacheKey: String { get }
}
And try to write code so every function that returns a response actually returns some ServerResponse
.
Then, for the consumers of ServerResponse
, I'd just maintain a separate path for types that conform to both ServerResponse
and CacheableResponse
:
struct ResponseHandler {
func handleResponse(_ response: some ServerResponse) {
// Common function for all paths
_handleResponse(response)
}
func handleResponse<Response: ServerResponse & CacheableResponse>(
_ response: Response
) {
// Do whatever you need with the cacheKey here...
print(response.cacheKey)
// Common function for all paths
_handleResponse(response)
}
private func _handleResponse(_ response: some ServerResponse) {
// Actually handle the response...
}
}