在 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=]) }
我有一个网络层 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=]) }