0
{
  "records": [
    {
      "id": 1,
      "customers_name": "Acme 1"
    },
    {
      "id": 2,
      "customers_name": "Acme2"
    }    
  ]
}

This is my really simple JSON scheme, but I can't get JSONDecoder() to work. My error code is:

Expected to decode Array but found a dictionary instead.

Here are my two files that I'm currently using:

Customer.swift

struct Customer: Decodable, Identifiable {

    public var id: String
    public var customers_name: String

    enum CodingKeys: String, CodingKey {
       case id = "id"
       case customers_name = "customers_name"
    }

    init(from decoder: Decoder) throws{
        let container = try decoder.container(keyedBy: CodingKeys.self)
            id = try container.decode(String.self, forKey: .id)
            customers_name = (try container.decodeIfPresent(String.self, forKey: .customers_name)) ?? "Unknown customer name"
    }
}

CustomerFetcher.swift

import Foundation

public class CustomerFetcher: ObservableObject {
    @Published var customers = [Customer]()

    init(){
        load()
    }

    func load() {
        let url = URL(string: "https://somedomain.com/customers.json")!

        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    print(d)
                    let decodedLists = try JSONDecoder().decode([Customer].self, from: d)
                    DispatchQueue.main.async {
                        self.customers = decodedLists
                    }
                } else {
                    print("No Data")
                }
            } catch {
                print (error)
            }

        }.resume()

    }
}

I believe it's because of this nested JSON structure and tried so many things, but still can't get it working.

Thank you so much, if anyone would help me out!

1 Answer 1

2

You are forgetting the wrapping object:

struct RecordList<T: Decodable>: Decodable {
    let records: [T]
}


let decodedLists = try JSONDecoder().decode(RecordList<Customer>.self, from: d)
DispatchQueue.main.async {
    self.customers = decodedLists.records
}

Also note the Customer can be reduced to:

struct Customer: Decodable, Identifiable {
   public var id: String
   public var customersName: String

   enum CodingKeys: String, CodingKey {
      case id
      case customersName = "customers_name"
   }
}

You can also setup your JSONDecoder to convert underscores to camel case automatically. Then you won't even need the CodingKeys.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.