在 swift 中将网络管理器转换为通用类型

convert network manager to generic type in swift

我有一个网络层 class 目前工作正常。我想提高代码质量和清洁递归代码。我所有的回复都是这样的:

{
    success: Int
    message: String
    id: Int
    data: [Comment]?
}

唯一可变的部分是数据。它可能是评论、商店、用户等。所以对于每个请求,我不想像那样写响应

struct ShopApiResponse {
    
    let success: Int
    let id: Int
    let message: String?
    let shops: [Shop]
}

extension ShopApiResponse: Decodable {
    
    private enum ShopApiResponseCodingKeys: String, CodingKey {
        case success = "Success"
        case id = "Id"
        case message = "Message"
        case shops = "Data"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: ShopApiResponseCodingKeys.self)
        
        success = try container.decode(Int.self, forKey: .success)
        id = try container.decode(Int.self, forKey: .id)
        message = (try container.decodeIfPresent(String.self, forKey: .message)) ?? ""
        shops = try container.decode([Shop].self, forKey: .shops)
        
    }
}

我想让它通用。它不应该是 getCommets(..), getShop(..) 等应该是 fetchData(...

这是获取商店的示例请求

func getShops( completion: @escaping (_ shop: [Shop]?,_ error: String?)->()){
    shopRouter.request(.getShops) { (data, response, error) in
        if error != nil {
            //completion(nil, "Please check your network connection.")
            completion(nil, error?.localizedDescription)

        }
        
        if let response = response as? HTTPURLResponse {
            let result = self.handleNetworkResponse(response)
            switch result {
            case .success:
                guard let responseData = data else {
                    completion(nil, NetworkResponse.noData.rawValue)
                    return
                }
                do {
                    let apiResponse = try JSONDecoder().decode(ShopApiResponse.self, from: responseData)
                    completion(apiResponse.shops,nil)
                    
                    
                }catch {
                    completion(nil, NetworkResponse.unableToDecode.rawValue)
                }
            case .failure(let networkFailureError):
                completion(nil, networkFailureError)
            }
        }
    }
}

并在 VC 中这样调用

networkManager.getShops { [weak self] (shops, error) in
//
}

当我尝试更改它时,模型显示无法推断通用参数 'T'

struct ResponseModel<T: Codable>: Codable {
    
    // MARK: - Properties
    var success: Int
    var message: String
    let id: Int
    var data: T?

    public init(from decoder: Decoder) throws {
        let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
        
        id = (try? keyedContainer.decode(Int.self, forKey: CodingKeys.id)) ?? 0
        success = (try? keyedContainer.decode(Int.self, forKey: CodingKeys.success)) ?? 0
        message = (try? keyedContainer.decode(String.self, forKey: CodingKeys.message)) ?? ""
        data = try? keyedContainer.decode(T.self, forKey: CodingKeys.data) 
    }
}

任何建议都会很棒。谢谢

为了更清楚我添加了更改功能

func sendRequest<T: Codable>(router: ShopApi, completion: @escaping(_ shop: ResponseModel<T>?, _ error: String?) ->()) {

    shopRouter.request(router) { (data, response, error) in
        if error != nil {
            completion(nil, error?.localizedDescription)
        }
        
        if let response = response as? HTTPURLResponse {
            let result = self.handleNetworkResponse(response)
            switch result {
            case .success:
                guard let responseData = data else {
                    completion(nil, NetworkResponse.noData.rawValue)
                    return
                }
                do {
                    let apiResponse = try JSONDecoder().decode(ResponseModel<T>.self, from: responseData)
                    completion(apiResponse,nil)
                }catch {
                    print(error)
                    completion(nil, NetworkResponse.unableToDecode.rawValue)
                }
            case .failure(let networkFailureError):
                completion(nil, networkFailureError)
            }
        }
    }
}

当我这样调用函数时

networkManager.sendRequest(router: .getShops) { (response, error) in
    print(error)
}

它说“无法推断通用参数 'T'”

为了让编译器解析泛型类型 T,它需要将其与具体的东西相匹配。例如:

func doSomething<T>(_ v: T) {}

// T is inferred to be Int
doSomething(5) 

在你的例子中,T 可以从完成处理程序的参数中推断出来:

func sendRequest<T>(_ completion: @escaping ((T) -> Void) {}

但是当你定义闭包时,你没有指定闭包参数是什么;实际上,您正在尝试推断它们。编译器在任何地方都没有 T 的具体类型来推断它。

这里有两种方法:

1 - 显式指定闭包的参数类型:

// T is inferred to be Int
sendRequest { (t: Int) -> Void in print(t) }

2 - 添加一个T.Type作为函数的参数:

func sendRequest<T>(_ type: T.Type, _ completion: @escaping ((T) -> Void)) {}

// T is inferred to be String
sendRequest(String.self) { print([=13=]) }