Swift:将类型从属性传递给泛型函数 [英] Swift: Pass type from property to generic function

查看:62
本文介绍了Swift:将类型从属性传递给泛型函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我的网络模块,我具有用于访问API不同部分的以下协议:

For my networking module, I have this protocol that I adopt for accessing different parts of the 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 }
}

我正在采用带有如下枚举的枚举:

I'm adopting this with enums that look like this:

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响应.每个响应都包含一个令牌和一个结果,其内容取决于请求路由.

I want to use Codable for decoding nested Api responses. Every response consists of a token and a result which content is depending on the request route.

对于发出请求,我想使用上面 enum responseResultType 属性中指定的类型.

For making the request I want to use the type specified in the responseResultType property in the enum above.

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 方法中,它引发了编译器错误(无法调用类型为'(Any.Type,from:Data)'的参数列表的'decode').我不能这样使用 ApiResponse< self.responseResultType!> .

But in my requestAndDecode method It throws an compiler error (Cannot invoke 'decode' with an argument list of type '(Any.Type, from: Data)'). I can't use ApiResponse<self.responseResultType!> like that.

我可以将此函数设为通用,然后这样调用:

I could make this function generic and call it like this:

TestRouter.getTestData(byId: 123).requestAndDecode(TestData.self, completion:)

但是随后我每次要使用此端点时都必须传递响应类型.

but then I'd have to pass the response type everytime I want to use this endpoint.

我要实现的是扩展功能 requestAndDecode 从自身( responseResultType 属性)获取响应类型信息.

What I want to achieve is that the extension function requestAndDecode takes it response type information from itself, the responseResultType property.

这可能吗?

推荐答案

忽略实际的错误报告,您对 requestAndDecode 有一个基本的问题:这是一个泛型函数,其类型参数在调用时确定声明返回值为 Result 类型的值的站点,但它尝试返回值为 unknown 类型的 self.responseResultType 类型的值

Ignoring the actual error report you have a fundamental problem with requestAndDecode: it is a generic function whose type parameters are determined at the call site which is declared to return a value of type Result yet it attempts to return a value of type self.responseResultType whose value is an unknown type.

如果Swift的类型系统支持此功能,则将需要运行时类型检查,潜在的失败,并且您的代码必须处理该问题.例如.您可以将 TestData 传递给 requestAndDecode ,而 responseResultType 可能是 ValidationResponse ...

If Swift's type system supported this it would require runtime type checking, potential failure, and your code would have to handle that. E.g. you could pass TestData to requestAndDecode while responseResultType might be ValidationResponse...

将JSON调用更改为:

Change the JSON call to:

JSONDecoder().decode(ApiResponse<Result>.self ...

并且类型静态匹配(即使 Result 的实际类型未知).

and the types statically match (even though the actual type that Result is is unknown).

您需要重新考虑您的设计.HTH

You need to rethink your design. HTH

这篇关于Swift:将类型从属性传递给泛型函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆