Swift:将类型从属性传递给泛型函数
Posted
技术标签:
【中文标题】Swift:将类型从属性传递给泛型函数【英文标题】:Swift: Pass type from property to generic function 【发布时间】:2018-07-05 09:01:06 【问题描述】:对于我的网络模块,我采用了这个协议来访问 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
属性中获取响应类型信息。
这可能吗?
【问题讨论】:
【参考方案1】:忽略实际的错误报告,requestAndDecode
存在一个根本问题:它是一个泛型函数,其类型参数在调用站点确定,它被声明为返回类型为 Result
的值,但它试图返回一个self.responseResultType
类型的值,其值为 unknown 类型。
如果 Swift 的类型系统支持这一点,它将需要运行时类型检查,潜在的失败,而您的代码将不得不处理它。例如。你可以将TestData
传递给requestAndDecode
而responseResultType
可能是ValidationResponse
...
将 JSON 调用更改为:
JSONDecoder().decode(ApiResponse<Result>.self ...
并且类型静态匹配(即使Result
的实际类型未知)。
您需要重新考虑您的设计。高温
【讨论】:
你说得对,我混合了函数的两个版本(第一个带有泛型,另一个没有)。我更新了代码。 感谢您的回答!我明白为什么这是不可能的。这个问题很相似,也有很好的解释(***.com/questions/30905060/…)【参考方案2】:使用 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)")
)
【讨论】:
以上是关于Swift:将类型从属性传递给泛型函数的主要内容,如果未能解决你的问题,请参考以下文章
从头认识java-13.11 对照数组与泛型容器,观察类型擦除给泛型容器带来什么问题?