使用协议的Swift通用解码器
我尝试使用protrocol为所有模型使用通用Json解码器 //以下是协议的定义:使用协议的Swift通用解码器,swift,protocols,Swift,Protocols,我尝试使用protrocol为所有模型使用通用Json解码器 //以下是协议的定义: func fetch<T: Decodable>(with request: URLRequest, decode: @escaping (Decodable) -> T?, completion: @escaping (Result<T, APIError>) -> Void) {.. other Code} func-fetch(带请求:URLRequest,解码:@e
func fetch<T: Decodable>(with request: URLRequest, decode: @escaping (Decodable) -> T?, completion: @escaping (Result<T, APIError>) -> Void) {.. other Code}
func-fetch(带请求:URLRequest,解码:@escaping(Decodable)->T?,完成:@escaping(Result)->Void){..other-code}
//以下是实施情况:
func getData(from endPoint: Endpoint, completion: @escaping (Result<ApiResponseArray<Codable>, APIError>) -> Void) {
let request = endPoint.request
fetch(with: request, decode: { json -> Decodable in
guard let dataResult = json as? modelData else { return nil }
return dataResult
}, completion: completion)
}
func getData(从端点:端点,完成:@escaping(Result)->Void){
let request=endPoint.request
获取(使用:request,解码:{json->Decodable in)
guard let dataResult=json作为?modelData else{return nil}
返回数据结果
},完成:完成)
}
ApiResponseArray给了我一个错误:协议类型“Codable”(又名“Decodable&Encodable”)不能符合“Decodable”,因为只有具体类型才能符合协议。但是,我如何实现一个通用解码器并将不同的模型传递给它们呢。我想我必须修改我的协议定义,但是如何修改呢?我希望传递模型,然后接收模型的解码数据(在我的示例modelData中)。很明显,当我编写以下代码时,程序会运行:
func getData(来自endPoint:endPoint,completion:@escaping(Result,apierro>)我的意思是当我使用具体的模型时,但是我想传递模型,这样我就可以对不同的模型使用该类
谢谢,
Arnold我可以建议您如何使用
Alamofire
在API调用结构中使用Decodable
我创建了RequestManager
类,该类继承自SessionManager
,并在其中添加了对所有人都通用的请求调用
class RequestManager: SessionManager {
// Create shared instance
static let shared = RequestManager()
// Create http headers
lazy var httpHeaders : HTTPHeaders = {
var httpHeader = HTTPHeaders()
httpHeader["Content-Type"] = "application/json"
httpHeader["Accept"] = "application/json"
return httpHeader
}()
//------------------------------------------------------------------------------
// MARK:-
// MARK:- Request Methods
//------------------------------------------------------------------------------
func responseRequest(_ url: String, method: Alamofire.HTTPMethod, parameter: Parameters? = nil, encoding: ParameterEncoding, header: HTTPHeaders? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Void {
self.request(url, method: method, parameters: parameter, encoding: encoding, headers: header).response { response in
completionHandler(response)
}
}
}
然后,在创建了一个以上的类之后,NetworkManager
类,该类包含所需的get/post方法调用,并通过JSONDecoder
对json进行解码,如下所示:
class NetworkManager {
static let shared = NetworkManager()
var progressVC : ProgressVC?
//----------------------------------------------------------------
// MARK:-
// MARK:- Get Request Method
//----------------------------------------------------------------
func getResponse<T: Decodable>(_ url: String, parameter: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, header: HTTPHeaders? = nil, showHUD: HUDFlag = .show, message: String? = "Please wait...", decodingType: T.Type, completion: @escaping (Decodable?, APIError?) -> Void) {
DispatchQueue.main.async {
self.showHideHud(showHUD: showHUD, message: "")
}
RequestManager.shared.responseRequest(url, method: .get, parameter: parameter, encoding: encoding, header: header) { response in
DispatchQueue.main.async {
self.showHideHud(showHUD: .hide, message: "")
}
guard let httpResponse = response.response else {
completion(nil, .requestFailed("Request Failed"))
return
}
if httpResponse.statusCode == 200 {
if let data = response.data {
do {
let genericModel = try JSONDecoder().decode(decodingType, from: data)
completion(genericModel, nil)
} catch {
do {
let error = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any]
if let message = error!["message"] as? String {
completion(nil, .errorMessage(message)!)
} else if let message = error!["message"] as? Int {
completion(nil, .errorMessage(String(describing: "Bad Request = \(message)")))
}
} catch {
completion(nil, .jsonConversionFailure("JSON Conversion Failure"))
}
}
} else {
completion(nil, .invalidData("Invalid Data"))
}
} else {
completion(nil, .responseUnsuccessful("Response Unsuccessful"))
}
}
}
}
我创建了以下枚举以发送数据或我的完成块中的错误
enum Result<T, U> where U: Error {
case success(T)
case failure(U)
}
之后,我将扩展这个DataManager
类来调用基于模块的web服务,因此我将创建Swift文件并扩展DataManager
类并调用相关API
参见下文,在API调用中,我将把相对模型返回到Result
likeResult
注意:-一些变量、模型和类是我的自定义。您可以将其替换为。协议不能符合自身,
Codable
必须是具体类型,或者只能用作泛型约束
在你的上下文中,你必须做后一种事情,类似这样的事情
func fetch<T: Decodable>(with request: URLRequest, decode: @escaping (Data) throws -> T, completion: @escaping (Result<T, APIError>) -> Void) { }
func getData<T: Decodable>(_ : T.Type = T.self, from endPoint: Endpoint, completion: @escaping (Result<T, APIError>) -> Void) {
let request = endPoint.request
fetch(with: request, decode: { data -> T in
return try JSONDecoder().decode(T.self, from: data)
}, completion: completion)
}
func fetch(带请求:URLRequest,解码:@escaping(Data)throws->T,完成:@escaping(Result)->Void){
func-getData(uquot.Type=T.self,from-endPoint:endPoint,completion:@escaping(Result)->Void){
let request=endPoint.request
获取(带:请求,解码:{data->T in)
返回try JSONDecoder().decode(T.self,from:data)
},完成:完成)
}
网络请求通常返回
数据
,这更合理,因为解码
闭包的参数类型阅读本系列文章:感谢您的提示。但也许您可以给我一个建议,如何编写完成处理程序来处理泛型模型。我刚刚学会的一个技巧使这一点变得更好:makegetData()
的第一个参数是\uuuu:T.Type=T.self
。这样,您就可以在函数签名中传递类型,而不必将其包含在闭包中(这通常有点难看)。但是,如果很容易推断,您就不必传递它。@RobNapier听起来确实不错。那么如何使用该函数来推断类型?如果您传递的是文本闭包,那么您将调用它getData(Something.self,from:endpoint){…})
,并且不需要将类型放入闭包中。如果要传递闭包变量(因此类型已知),可以调用getData(from:endpoint,completion:completion)
。
enum APIError: Error {
case errorMessage(String)
case requestFailed(String)
case jsonConversionFailure(String)
case invalidData(String)
case responseUnsuccessful(String)
case jsonParsingFailure(String)
var localizedDescription: String {
switch self {
case.errorMessage(let msg):
return msg
case .requestFailed(let msg):
return msg
case .jsonConversionFailure(let msg):
return msg
case .invalidData(let msg):
return msg
case .responseUnsuccessful(let msg):
return msg
case .jsonParsingFailure(let msg):
return msg
}
}
}
extension DataManager {
// MARK:- Store List
func getStoreList(completion: @escaping (Result<StoreListModel, APIError>) -> Void) {
NetworkManager.shared.getResponse(getURL(.storeList), parameter: nil, encoding: JSONEncoding.default, header: getHeaders("bd_suvlascentralpos"), showHUD: .show, message: "Please wait...", decodingType: StoreListModel.self) { (decodableData, apiError) in
if apiError != nil {
completion(.failure(apiError!))
} else {
guard let userData = decodableData as? StoreListModel else {
completion(.failure(apiError!))
return
}
completion(.success(userData))
}
}
}
}
DataManager.shared.getStoreList { (result) in
switch result {
case .success(let storeListModel):
if let storeList = storeListModel, storeList.count > 0 {
self.arrStoreList = storeList
self.tblStoreList.isHidden = false
self.labelEmptyData.isHidden = true
self.tblStoreList.reloadData()
} else {
self.tblStoreList.isHidden = true
self.labelEmptyData.isHidden = false
}
break
case .failure(let error):
print(error.localizedDescription)
break
}
}
func fetch<T: Decodable>(with request: URLRequest, decode: @escaping (Data) throws -> T, completion: @escaping (Result<T, APIError>) -> Void) { }
func getData<T: Decodable>(_ : T.Type = T.self, from endPoint: Endpoint, completion: @escaping (Result<T, APIError>) -> Void) {
let request = endPoint.request
fetch(with: request, decode: { data -> T in
return try JSONDecoder().decode(T.self, from: data)
}, completion: completion)
}