簡體   English   中英

Swift:將類型從屬性傳遞給泛型函數

[英]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 響應。 每個響應都包含一個令牌和一個結果,其內容取決於請求路由。

為了發出請求,我想使用上面enumresponseResultType屬性中指定的類型。

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傳遞給requestAndDecoderesponseResultType可能是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM