I m using this code to call my rest web service. But if I try to decode the result of web service call I received error.
class func callPostServiceReturnJson(apiUrl urlString: String, parameters params : [String: AnyObject]?, parentViewController parentVC: UIViewController, successBlock success : @escaping ( _ responseData : AnyObject, _ message: String) -> Void, failureBlock failure: @escaping (_ error: Error) -> Void) {
if Utility.checkNetworkConnectivityWithDisplayAlert(isShowAlert: true) {
var strMainUrl:String! = urlString "?"
for dicd in params! {
strMainUrl.append("\(dicd.key)=\(dicd.value)&")
}
print("Print Rest API : \(strMainUrl ?? "")")
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 120
manager.request(urlString, method: .get, parameters: params)
.responseJSON {
response in
switch (response.result) {
case .success:
do{
let users = try JSONDecoder().decode(OrderStore.self, from: response.result.value! as! Data)
}catch{
print("errore durante la decodifica dei dati: \(error)")
}
if((response.result.value) != nil) {
success(response as AnyObject, "Successfull")
}
break
case .failure(let error):
print(error)
if error._code == NSURLErrorTimedOut {
//HANDLE TIMEOUT HERE
print(error.localizedDescription)
failure(error)
} else {
print("\n\nAuth request failed with error:\n \(error)")
failure(error)
}
break
}
}
} else {
parentVC.hideProgressBar();
Utility.showAlertMessage(withTitle: EMPTY_STRING, message: NETWORK_ERROR_MSG, delegate: nil, parentViewController: parentVC)
}
}
This is the error that I can print:
Could not cast value of type '__NSDictionaryI' (0x7fff86d70b80) to 'NSData' (0x7fff86d711e8).
2021-09-27 16:34:49.810245 0200 ArrivaArrivaStore[15017:380373] Could not cast value of type '__NSDictionaryI' (0x7fff86d70b80) to 'NSData' (0x7fff86d711e8).
Could not cast value of type '__NSDictionaryI' (0x7fff86d70b80) to 'NSData' (0x7fff86d711e8).
CoreSimulator 732.18.6 - Device: iPhone 8 (6F09ED5B-8607-4E47-8E2E-A89243B9BA90) - Runtime: iOS 14.4 (18D46) - DeviceType: iPhone 8
I generated OrderStore.swift class from
CodePudding user response:
.responseJSON
returns deserialized JSON, in this case a Dictionary
. It cannot be cast to Data
what the error clearly confirms.
To get the raw data you have to specify .responseData
Replace
.responseJSON {
response in
switch (response.result) {
case .success:
do {
let users = try JSONDecoder().decode(OrderStore.self, from: response.result.value! as! Data)
with
.responseData {
response in
switch response.result {
case .success(let data):
do {
let users = try JSONDecoder().decode(OrderStore.self, from: data)
Consider that AF 5 supports even .responseDecodable
to decode directly into the model
.responseDecodable {
(response : DataResponse<OrderStore,AFError>) in
switch response.result {
case .success(let users): print(users)
Side notes:
As mentioned in your previous question there is no
AnyObject
in the AF API. The parameters are[String:Any]
andresponseData
is the decoded type. I recommend to make the function generic and use the convenientResult
type.Delete the
break
statements. This is Swift.
CodePudding user response:
This is an addendum to Vadian's answer. I'm trying to illustrate the process that lead you into this error, with the hopes that you can notice it in the future, before it leads you astray
This is a pretty common "pattern" of error.
Picture it as though you're traversing a maze, starting from some initial data format, and trying to get to some destination data format. At each point along the way, there are several options to choose from, some which get you closer to your goal, and some which lead you further away.
You've chosen to enter the maze at the entryway called responseJSON
, whose callback will give you a AFDownloadResponse<Any>
(which is the inferred type of the variable you called response
).
JSON structures always have an array or dictionary at the top level. Since Alamofire can't statically know which kind of JSON you'll be dealing with, it models this with an Any
. At runtime, the type of the Value
will be either NSDictionary
(or one of its concrete subclasses, like __NSDictionaryI
) or NSArray
(or one of its concrete subclasses).
You then decide to get the result
of that response
. Its static type is Result<Any, Error>
. You switch
over this error, ensuring you're dealing with the success
case and not the failure
case. Inexplicably, you ignore the payload value associated with the success, but later force unwrap it out with result.response.value!
.
result.response.value
is an Any
, but to placate the compiler your force-cast it to a Data
. But we already know this will only ever be an NSArray
or NSDictionary
, so this will never work.
You could keep wandering around in this area of the maze, and stumble to the end goal via a long path. For example, you could force cast to NSDictionary
, then re-serialize that dictionary structure back to a JSON string, which you can turn into Data
, only for you to then pass it to JSONDecoder().decode
, which will then decode that JSON back. Of course, this is all awfully round-about and wasteful. The issue was the that responseJSON
maze entrance was not the right one for where you're trying to go!
You could have instead entered into the responseData
maze entrance, which gets you right to your Data
destination!
Though you might then realize that the Data
was a red herring all along. You didn't actually want Data
. You wanted to decode an OrderStore
, and Data
was how you thought you needed to get there. But it turns out that so many people were entering through the Data
entrance with the intent to decode some JSON, that the Alamofire people carved out a new entrance just for you: responseDecodable
. It takes you right to the OrderStore
, and fiddles around with the JSON, Data
or whatever, under the hood where you don't have to worry about it.