Swift: associatedtype
A great example use case of using Swift’s associatedtype.
I like how this pattern:
- binds the request and response types at compile time
- lets the implementation of
submit
be ignorant of the response type; it just needs to know that it’s theassociatedtype
of the request - lets the caller of
submit
not have to explicitly state the response type but still access its data
enum APIRequestMethod {
case get
case post
}
protocol APIRequest {
associatedtype Response: Decodable
var path: String { get }
var method: APIRequestMethod { get }
var parameters: [String: Any] { get }
}
struct APILoginRequest: APIRequest {
let path: String = "/api/login"
let method: APIRequestMethod
let parameters: [String : Any]
struct Response: Decodable {
let username: String
let userToken: String
}
}
enum APIClientResponse<T> {
case success(T)
case error(Error)
}
enum APIClientError: Error {
case invalidData
}
class APIClient {
func submit<T>(_ request: T, complete: (APIClientResponse<T.Response>) -> Void) where T: APIRequest {
let decoder = JSONDecoder()
let fakeResponseData = "{\"username\": \"fake_dude\", \"userToken\": \"fake_token\"}".data(using: .utf8)
guard let data = fakeResponseData else {
complete(.error(APIClientError.invalidData))
return
}
do {
let response: T.Response = try decoder.decode(T.Response.self, from: data)
complete(.success(response))
} catch {
complete(.error(error))
}
}
}
let client = APIClient()
let request = APILoginRequest(method: .get, parameters: [:])
client.submit(request) { (response) in
switch response {
case .success(let response):
print("login success: \(response.username) [\(response.userToken)]")
case .error(let error):
print("error: \(error.localizedDescription)")
}
}
Thanks to Frank Carnevale for inspiration for this post.