根据路径更改响应模型/形状
Change response model / shape depending on path
我有一个用于调用多个端点的网络层。我想减少重复代码的数量,并认为也许我可以将我的响应模型作为我的端点的一部分传递。
我的想法是不需要多个函数,只是响应不同,我可以调用我的网络层并根据路径进行设置。
我目前看到的错误是
Var 'responseType' is not a member type of 'IdentityEndpoint'
我希望实现这样的目标
mutating func identity(with endpoint: IdentityEndpoint, completion: @escaping (Either<IdentityEndpoint.responseType>) -> Void)
而不是这个
mutating func identity(with endpoint: IdentityEndpoint, completion: @escaping (Either<OAuthToken>) -> Void)
APIClient
struct APIClient: APIClientProtocol {
var task: URLSessionDataTask = URLSessionDataTask()
var session: SessionProtocol = URLSession.shared
var request: URLRequest?
mutating func identity(with endpoint: IdentityEndpoint, completion: @escaping (Either<IdentityEndpoint.responseType>) -> Void) {
dispatch(endpoint: endpoint, completion: completion)
}
}
extension APIClient {
fileprivate mutating func dispatch<T: Codable>(endpoint: EndpointProtocol, completion: @escaping (Either<T>) -> Void) {
do {
request = try constructRequest(from: endpoint)
guard let request = request else { return }
call(with: request, completion: completion)
} catch {}
}
fileprivate func constructRequest(from route: EndpointProtocol) throws -> URLRequest {
var request = URLRequest(url: route.baseUrl.appendingPathComponent(route.path), cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0)
request.httpMethod = route.httpMethod.rawValue
do {
switch route.task {
case .request(let headers):
addAdditionalHeaders(headers, request: &request)
case .requestParams(let bodyParams, let encoding, let urlParams, let headers):
addAdditionalHeaders(headers, request: &request)
try configureParameters(bodyParams: bodyParams, encoding: encoding, urlParams: urlParams, request: &request)
}
return request
} catch {
throw NSError(domain: "Could not create request task for \(route.task)", code: 0, userInfo: nil)
}
}
fileprivate func configureParameters(bodyParams: Parameters?, encoding: ParameterEncoding, urlParams: Parameters?, request: inout URLRequest) throws {
do {
try encoding.encode(urlRequest: &request, bodyParams: bodyParams, urlParams: urlParams)
} catch {
throw NSError(domain: "Could not configure params for request", code: 0, userInfo: nil)
}
}
fileprivate func addAdditionalHeaders(_ additionalHeaders: HTTPHeaders?, request: inout URLRequest) {
guard let headers = additionalHeaders else { return }
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
}
}
IdentityEndPoint
protocol EndpointProtocol {
var baseUrl: URL { get }
var path: String { get }
var httpMethod: HTTPMethod { get }
var task: HTTPTask { get }
var headers: HTTPHeaders? { get }
}
public enum IdentityEndpoint {
case accessToken(company: String, code: String)
func getDomain(forService service: String) -> URL {
return URL(string: "https://{SERVICE}.foo.bar".replacingOccurrences(of: "{SERVICE}", with: service))!
}
}
extension IdentityEndpoint: EndpointProtocol {
var baseUrl: URL {
return getDomain(forService: "identity")
}
var responseType: Codable {
switch self {
default:
return OAuthToken.self as! Codable
}
}
var path: String {
switch self {
case .accessToken(let props):
return "/auth/realms/\(props.company)/protocol/openid-connect/token"
}
}
var httpMethod: HTTPMethod {
switch self {
case .accessToken:
return .POST
}
}
var headers: HTTPHeaders? {
switch self {
case .accessToken:
return ["Content-Type": "application/x-www-form-urlencoded"]
}
}
var task: HTTPTask {
switch self {
case .accessToken(let props):
return .requestParams(bodyParams: [
"grant_type": "authorization_code", "code": "\(props.code)", "redirect_uri": "homedev://oauth-callback", "client_id": "mobile-home"
], encoding: .jsonEncoding, urlParams: nil, headers: headers)
}
}
}
将 associatedtype
添加到您的 EndpointProtocol
。然后在IdentityEndpoint
中这样指定
protocol EndpointProtocol {
associatedtype ResponseType
...
}
extension IdentityEndpoint: EndpointProtocol {
typealias ResponseType = OAuthToken
...
}
现在你可以写了
mutating func identity(
with endpoint: IdentityEndpoint,
completion: @escaping (Either<IdentityEndpoint.ResponseType>) -> Void
)
我有一个用于调用多个端点的网络层。我想减少重复代码的数量,并认为也许我可以将我的响应模型作为我的端点的一部分传递。
我的想法是不需要多个函数,只是响应不同,我可以调用我的网络层并根据路径进行设置。
我目前看到的错误是
Var 'responseType' is not a member type of 'IdentityEndpoint'
我希望实现这样的目标
mutating func identity(with endpoint: IdentityEndpoint, completion: @escaping (Either<IdentityEndpoint.responseType>) -> Void)
而不是这个
mutating func identity(with endpoint: IdentityEndpoint, completion: @escaping (Either<OAuthToken>) -> Void)
APIClient
struct APIClient: APIClientProtocol {
var task: URLSessionDataTask = URLSessionDataTask()
var session: SessionProtocol = URLSession.shared
var request: URLRequest?
mutating func identity(with endpoint: IdentityEndpoint, completion: @escaping (Either<IdentityEndpoint.responseType>) -> Void) {
dispatch(endpoint: endpoint, completion: completion)
}
}
extension APIClient {
fileprivate mutating func dispatch<T: Codable>(endpoint: EndpointProtocol, completion: @escaping (Either<T>) -> Void) {
do {
request = try constructRequest(from: endpoint)
guard let request = request else { return }
call(with: request, completion: completion)
} catch {}
}
fileprivate func constructRequest(from route: EndpointProtocol) throws -> URLRequest {
var request = URLRequest(url: route.baseUrl.appendingPathComponent(route.path), cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0)
request.httpMethod = route.httpMethod.rawValue
do {
switch route.task {
case .request(let headers):
addAdditionalHeaders(headers, request: &request)
case .requestParams(let bodyParams, let encoding, let urlParams, let headers):
addAdditionalHeaders(headers, request: &request)
try configureParameters(bodyParams: bodyParams, encoding: encoding, urlParams: urlParams, request: &request)
}
return request
} catch {
throw NSError(domain: "Could not create request task for \(route.task)", code: 0, userInfo: nil)
}
}
fileprivate func configureParameters(bodyParams: Parameters?, encoding: ParameterEncoding, urlParams: Parameters?, request: inout URLRequest) throws {
do {
try encoding.encode(urlRequest: &request, bodyParams: bodyParams, urlParams: urlParams)
} catch {
throw NSError(domain: "Could not configure params for request", code: 0, userInfo: nil)
}
}
fileprivate func addAdditionalHeaders(_ additionalHeaders: HTTPHeaders?, request: inout URLRequest) {
guard let headers = additionalHeaders else { return }
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
}
}
IdentityEndPoint
protocol EndpointProtocol {
var baseUrl: URL { get }
var path: String { get }
var httpMethod: HTTPMethod { get }
var task: HTTPTask { get }
var headers: HTTPHeaders? { get }
}
public enum IdentityEndpoint {
case accessToken(company: String, code: String)
func getDomain(forService service: String) -> URL {
return URL(string: "https://{SERVICE}.foo.bar".replacingOccurrences(of: "{SERVICE}", with: service))!
}
}
extension IdentityEndpoint: EndpointProtocol {
var baseUrl: URL {
return getDomain(forService: "identity")
}
var responseType: Codable {
switch self {
default:
return OAuthToken.self as! Codable
}
}
var path: String {
switch self {
case .accessToken(let props):
return "/auth/realms/\(props.company)/protocol/openid-connect/token"
}
}
var httpMethod: HTTPMethod {
switch self {
case .accessToken:
return .POST
}
}
var headers: HTTPHeaders? {
switch self {
case .accessToken:
return ["Content-Type": "application/x-www-form-urlencoded"]
}
}
var task: HTTPTask {
switch self {
case .accessToken(let props):
return .requestParams(bodyParams: [
"grant_type": "authorization_code", "code": "\(props.code)", "redirect_uri": "homedev://oauth-callback", "client_id": "mobile-home"
], encoding: .jsonEncoding, urlParams: nil, headers: headers)
}
}
}
将 associatedtype
添加到您的 EndpointProtocol
。然后在IdentityEndpoint
中这样指定
protocol EndpointProtocol {
associatedtype ResponseType
...
}
extension IdentityEndpoint: EndpointProtocol {
typealias ResponseType = OAuthToken
...
}
现在你可以写了
mutating func identity(
with endpoint: IdentityEndpoint,
completion: @escaping (Either<IdentityEndpoint.ResponseType>) -> Void
)