[英]Swift: Pass type from property to generic function
對於我的網絡模塊,我采用了這個協議來訪問 API 的不同部分:
protocol Router: URLRequestConvertible {
var baseUrl: URL { get }
var route: Route { get }
var method: HTTPMethod { get }
var headers: [String: String]? { get }
var encoding: ParameterEncoding? { get }
var responseResultType: Decodable.Type? { get }
}
我正在采用看起來像這樣的枚舉:
enum TestRouter: Router {
case getTestData(byId: Int)
case updateTestData(byId: Int)
var route: Route {
switch self {
case .getTestData(let id): return Route(path: "/testData/\(id)")
case .updateTestData(let id): return Route(path: "/testDataOtherPath/\(id)")
}
}
var method: HTTPMethod {
switch self {
case .getTestData: return .get
case .updateTestData: return .put
}
}
var headers: [String : String]? {
return [:]
}
var encoding: ParameterEncoding? {
return URLEncoding.default
}
var responseResultType: Decodable.Type? {
switch self {
case .getTestData: return TestData.self
case .updateTestData: return ValidationResponse.self
}
}
}
我想使用Codable
來解碼嵌套的 Api 響應。 每個響應都包含一個令牌和一個結果,其內容取決於請求路由。
為了發出請求,我想使用上面enum
的responseResultType
屬性中指定的類型。
struct ApiResponse<Result: Decodable>: Decodable {
let token: String
let result: Result
}
extension Router {
func asURLRequest() throws -> URLRequest {
// Construct URL
var completeUrl = baseUrl.appendingPathComponent(route.path, isDirectory: false)
completeUrl = URL(string: completeUrl.absoluteString.removingPercentEncoding ?? "")!
// Create URL Request...
var urlRequest = URLRequest(url: completeUrl)
// ... with Method
urlRequest.httpMethod = method.rawValue
// Add headers
headers?.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key) }
// Encode URL Request with the parameters
if encoding != nil {
return try encoding!.encode(urlRequest, with: route.parameters)
} else {
return urlRequest
}
}
func requestAndDecode(completion: @escaping (Result?) -> Void) {
NetworkAdapter.sessionManager.request(urlRequest).validate().responseData { response in
let responseObject = try? JSONDecoder().decode(ApiResponse<self.responseResultType!>, from: response.data!)
completion(responseObject.result)
}
}
}
但是在我的requestAndDecode
方法中,它會引發編譯器錯誤( Cannot invoke 'decode' with an argument list of type '(Any.Type, from: Data)'
)。 我不能像那樣使用ApiResponse<self.responseResultType!>
。
我可以使這個函數通用並像這樣調用它:
TestRouter.getTestData(byId: 123).requestAndDecode(TestData.self, completion:)
但是每次我想使用這個端點時我都必須傳遞響應類型。
我想要實現的是擴展函數requestAndDecode
從它自身獲取響應類型信息,即responseResultType
屬性。
這可能嗎?
忽略實際的錯誤報告, requestAndDecode
有一個基本問題:它是一個泛型函數,其類型參數在調用站點確定,聲明返回Result
類型的值,但它嘗試返回self.responseResultType
類型的值值是未知類型。
如果 Swift 的類型系統支持此功能,則需要運行時類型檢查、潛在故障,並且您的代碼必須處理這些問題。 例如,您可以將TestData
傳遞給requestAndDecode
而responseResultType
可能是ValidationResponse
...
將 JSON 調用更改為:
JSONDecoder().decode(ApiResponse<Result>.self ...
並且類型靜態匹配(即使Result
的實際類型未知)。
你需要重新考慮你的設計。 HTH
使用Combine 和AlomFire 創建一個通用函數。 您可以將它用於所有方法(獲取、發布、放置、刪除)
func fatchData<T: Codable>(requestType: String, url: String, params: [String : Any]?, myType: T.Type, completion: @escaping (Result<T, Error>) -> Void) {
var method = HTTPMethod.get
switch requestType {
case "Get":
method = HTTPMethod.get
case "Post":
method = HTTPMethod.post
print("requestType \(requestType) \(method) ")
case "Put":
method = HTTPMethod.put
default:
method = HTTPMethod.delete
}
print("url \(url) \(method) \(AppConstant.headers) ")
task = AF.request(url, method: method, parameters: params, encoding: JSONEncoding.default, headers: AppConstant.headers)
.publishDecodable(type: myType.self)
.sink(receiveCompletion: { (completion) in
switch completion{
case .finished:
()
case .failure(let error):
// completion(.failure(error))
print("error \(error)")
}
}, receiveValue: {
[weak self ](response) in
print("response \(response)")
switch response.result{
case .success(let model):
completion(.success(model))
print("error success")
case .failure(let error):
completion(.failure(error))
print("error failure \(error.localizedDescription)")
}
}
)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.