Home > OS >  Could not cast value of type '__NSDictionaryI'
Could not cast value of type '__NSDictionaryI'

Time:09-28

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 enter image description here

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] and responseData is the decoded type. I recommend to make the function generic and use the convenient Result 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.

  • Related