Sorry for the non descriptive title, but basically I'm looking for guidance on implementing an API for a request/response system, where I want the endpoint, request and response types to be tied up so that it fails to compile if you use the wrong combination.
As an example, I have the following endpoints with their respective request and response types:
enum Endpoint {
case createUser
case addTodo
}
where the createUser
endpoint (on the backend) accepts a JSON object, and returns another JSON object, that can be modeled by:
struct CreateUserRequest: Encodable {
let name: String
let age: Int
}
struct CreateUserResponse: Decodable {
let userID: String
}
Imagine something similar for addTodo
but with AddTodoRequest
and AddTodoResponse
which also conform to Encodable/Decodable as needed.
What I want is to have a single API for making this request that checks that the endpoint and the expected types are correct at compile time.
What I have right now is this solution:
func call<Request: Encodable, Response: Decodable>(_ typedFunction: TypedFunction<Request, Response>, completion: ((Result<Response, Error>) -> Void)? = nil) {
let data = // request to data with JSONEncoder
let task = // send with URLSession and convert data to Response with JSONDecoder {
// Call completion with typed Result<Response, Error> depending on http response.
}
}
struct TypedFunction<Request: Encodable, Response: Decodable> {
let endpoint: Endpoint
let request: Request
init(endpoint: Endpoint, request: Request) {
self.endpoint = endpoint
self.request = request
}
}
// In another file...
struct CreateUserRequest: Encodable { ... }
struct CreateUserResponse: Decodable { ... }
extension TypedFunction {
static func createUser(
_ request: CreateUserRequest
) -> TypedFunction<CreateUserRequest, CreateUserResponse> {
return TypedFunction<CreateUserRequest, CreateUserResponse>(
endpoint: .createUser,
request: request
)
}
}
which allows me to do:
Executor.call(.createUser(CreateUserRequest(name..., age...))) {
print(try! result.get().userID)
}
This is the closest I've gotten without asking for help, but I feel like this must be a very common pattern with some name which I've yet to encounter, and also wondering if there are better architectures for this type of solution.
Thanks for any feedback!