I’m not entirely sure that I understand the ask, but this compiles and might be what you want:
protocol BaseRequest {
associatedtype DataType
var call0: Single<Data> { get }
}
struct ApiResult: Decodable { }
struct Api: BaseRequest {
typealias DataType = [ApiResult]
var call0: RxSwift.Single<Data>
}
extension BaseRequest where DataType == [ApiResult] {
var call: Single<DataType?> {
self.call0.map { DataType.deserialize(from: $0) }
}
}
extension Array where Element == ApiResult {
static func deserialize(from data: Data) -> [ApiResult] {
(try? JSONDecoder().decode([ApiResult].self, from: data)) ?? []
}
}
let api = Api(call0: .just(Data()))
api.call.subscribe(onSuccess: { result in
// `result` is an `[ApiResult]` as you wanted.
})
That said, this feels like a very strange setup. I prefer something more like this:
// this part is the custom code for your endpoint
struct ApiResult: Decodable { }
extension Endpoint where Response == [ApiResult] {
static var apiResults: Endpoint {
// make your request here
let request = URLRequest(url: URL(string: "whatever")!)
return Endpoint(request: request, decoder: JSONDecoder())
}
}
let api = Api(data: URLSession.shared.rx.data(request:)) // pass in a mock for testing.
api.call(endpoint: .apiResults)
.subscribe(onSuccess: { apiResults in
// here you go.
})
// this part is the reusable library. It will work for every conceivable
// REST request, no matter how complex.
struct Endpoint<Response> {
let request: URLRequest
let response: (Data) throws -> Response
init(request: URLRequest, response: @escaping (Data) throws -> Response) {
self.request = request
self.response = response
}
}
extension Endpoint where Response: Decodable {
init(request: URLRequest, decoder: TopLevelDecoder) {
self.request = request
self.response = { try decoder.decode(Response.self, from: $0) }
}
}
extension Endpoint where Response == Void {
init(request: URLRequest) {
self.request = request
self.response = { _ in }
}
}
class Api {
let data: (URLRequest) -> Observable<Data>
init(data: @escaping (URLRequest) -> Observable<Data>) {
self.data = data
}
func call<Response>(endpoint: Endpoint<Response>) -> Single<Response> {
data(endpoint.request)
.map(endpoint.response)
.asSingle()
}
}