0

I'm pretty sure that my problem is easy solving, but I can't find any solution. So I have Alamofire request and have a trouble with handling data types. I have so many 'printing out' just to check what data I've got step by step.

Alamofire.request(URL, method: .get, headers: headers).responseJSON { response in
            switch responseJSON.result {
            case .success(let value):
                print(type(of: value))  //__NSDictionaryI
                print(value)
                print(type(of:responseJSON))  //DataResponse<Any>
                print(responseJSON) . //SUCCESS: {"billing_addresses" =     (...
                print(responseJSON.value as Any) . //Optional({...
                //print(responseJSON.value as! [[String:Any]]) . //Could not cast value of type '__NSDictionaryI' (0x10b9fb508) to 'NSArray' (0x10b9fb008).

                do {
                    let decoder = JSONDecoder()
                    let model = try decoder.decode(Info.self, from: value as! Data) //Decode JSON Response Data
                    print(model.id)
                } catch let parsingError {
                    print("Error", parsingError)
                }

Now I have an error: **Could not cast value of type '__NSSingleEntryDictionaryI' (0x10d240f78) to 'NSData' (0x10d241090).**

value of responseJSON is:

(I'm not sure that this value is correct, because when I've check in Postman all strings is doublequoted, and value of "is_default" is true/false, not 0/1. But in the Xcode I've got this in the console. So maybe problem in the responseJSON?..)

And there could be zero address, or several ones.

{
"id": 40128,
"username": "test6",
"email": "test6@on.com",
"billing_addresses": [
    {
        "address_name": null,
        "country_code": "US",
        "first_name": "Ted",
        "last_name": "Qqqq",
        "company_name": "",
        "address_line1": "308 Sea Lane",
        "address_line2": "",
        "city": "QQQQ",
        "state": "FL",
        "postcode": "32000",
        "email_address": "test6@on.com",
        "phone_number": "11111111",
        "is_default_for_billing": true
    }
],
"shipping_addresses": [
    {
        "address_name": null,
        "country_code": "US",
        "first_name": "Ted",
        "last_name": "Qqqq",
        "company_name": "",
        "address_line1": "308 Sea Lane",
        "address_line2": "",
        "city": "QQQQ",
        "state": "FL",
        "postcode": "32000",
        "is_default_for_shipping": true
    }
]

}

And here is model

struct Info : Decodable {
                    let id: Int
                    let email: String
                    let username: String
                    let billing_addresses: Billings
                    let shipping_addresses: Shippings
                }
                struct Billings: Decodable{
                    let address_name: String
                    let country_code: String
                    let first_name: String
                    let last_name: String
                    let company_name: String
                    let address_line1: String
                    let address_line2: String
                    let city: String
                    let state: String
                    let postcode: String
                    let email_address: String
                    let phone_number: String
                    let is_default_for_billing: Bool

                }
                struct Shippings:Decodable{
                    let address_name: String
                    let country_code: String
                    let first_name: String
                    let last_name: String
                    let company_name: String
                    let address_line1: String
                    let address_line2: String
                    let city: String
                    let state: String
                    let postcode: String
                    let is_default_for_shipping: Bool

                }

If I try to use SwiftyJSON with value as parameter I have an error that Any couldn't be Data and I really don't know what should I do.

nastassia
  • 807
  • 1
  • 12
  • 31

1 Answers1

3

responseJSON.result.value returns the deserialized collection type, in your case a dictionary [String:Any]

To use JSONDecoder you need the raw data which is in response.data

let model = try decoder.decode(Info.self, from: response.data) //Decode JSON Response Data

Consider that you will run into decoding errors: billing_addresses and shipping_addresses are arrays

let billing_addresses: [Billings] 
let shipping_addresses: [Shippings] // better name both structs in singular form (Billing, Shipping)

and a few values could be numbers rather than strings.

Anyway it's recommended to use the convertFromSnakeCase key decoding strategy to get rid of the ugly snake_case names.

Edit:

Here are your structs with camelCased names and singular forms, you have to add

decoder.keyDecodingStrategy = .convertFromSnakeCase

struct Info : Decodable {
    let id: Int
    let email: String
    let username: String
    let billingAddresses: [Billing]
    let shippingAddresses: [Shipping]
}

struct Billing : Decodable {
    let addressName: String?
    let countryCode, firstName, lastName, companyName: String
    let addressLine1, addressLine2, city, state, postcode: String
    let emailAddress, phoneNumber: String
    let isDefaultForBilling: Bool

}
struct Shipping : Decodable {
    let addressName: String?
    let countryCode, firstName, lastName, companyName: String
    let addressLine1, addressLine2, city, state, postcode: String
    let isDefaultForShipping: Bool
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • if I change line to 'from: response.data!' then I have an Error: "Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}))) " – nastassia Aug 23 '18 at 15:15
  • big thanks for notice that there should be arrays in the structs! – nastassia Aug 23 '18 at 15:16
  • Then try `responseData` instead of `responseJSON` and pass `response.result.value` as argument in JSONDecoder – vadian Aug 23 '18 at 15:18
  • I can pass only `responseData.result.value!` and have an Error `Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))'`If I'm passing `response.result.value` Xcode shows warning `Use of unresolved identifier 'response'` – nastassia Aug 23 '18 at 15:30
  • No, please read my last comment: It's `response.result.value`, not `responseData.result.value`. And In the Alamofire API it's `headers).responseData { response` – vadian Aug 23 '18 at 15:33
  • In this case `Error valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "billing_addresses", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "address_name", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil))`. And if printing out: `print(response) //SUCCESS: 655 bytes` and that is all – nastassia Aug 23 '18 at 15:40
  • and printing out `print(response.result.value) //Optional(655 bytes)` – nastassia Aug 23 '18 at 15:42
  • Basically that's good, but you got one of the predicted decoding errors. Make `address_name` in `Billings` and `Shippings` optional: `let address_name: String?` – vadian Aug 23 '18 at 15:44
  • And still have `Error dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))` – nastassia Aug 23 '18 at 15:56
  • If you get the decoding error you can't get corrupted data at the same time. – vadian Aug 23 '18 at 15:57
  • thank you so much @vadian I'm so appreciate you trying to help me! – nastassia Aug 23 '18 at 15:57
  • The decoder is suppose to work if both `address_name` properties are optional (`String?`). I added the camelCased structs and tested it quick&dirty. – vadian Aug 23 '18 at 16:04
  • oh my! I've just commented this 2 lines with address names and it FINALLY WORKS! Oh you really save me! THANK YOU!!! I guess better there should be an empty string and everything will be ok – nastassia Aug 23 '18 at 16:12
  • Thank you for your Answer! Loved It! you saved me a bunch of time! – user2525211 Aug 08 '20 at 21:20