Search code examples
iosswift5xcode11xcode12

Swift 5 How do we parse a Dictionary nested inside a Dictionary, nested inside another Dictionary?


This is the json at hand. I am able to parse all the data except the "clientDetails" dictionary. Here is my code. Im just not sure how to go about mapping the "clientDetails" dictionary. Is there a completely different approach i should be taking? or am I on the right track here..

{
  "data": {
    "userID": "124",
    "techID": "1233",
    "clientID": "1",
    "Name": "Haibert",
    "lastName": "Chiem",
    "emailAddress": "[email protected]",
    "additionalCompanies": "",
    "clientDetails": {
      "clientID": "1",
      "FirstName": "Sam",
      "LastName": "Ka",
      "email": "[email protected]",
      "CustomerName": "SimplyRem",
      "BusinessAddress": "9863 N. Glenoaks Blvd, Ste. 207F",
      "BusinessCity": "Burbank",
      "BusinessState": "",
      "BusinessZip": "91502",
      "BusinessCountry": "USA",
      "BusinessPhone": "(818)457-9507",
      "BusinessFax": null,
      "MobilePhone": "818-378-9507",
      "ContactName": "Samuel Kaz",
      "ContactTitle": "Owner",
      "ContactPhone": "818-624-9507",
      "ContactEmail": "[email protected]",
      "lastUpdated": "2020-10-24 15:09:17"
    }
  }
}

struct UserInformation: Decodable {
    var userID: String = ""
    var clientID: String = ""
    var Name: String = ""
    var lastName: String = ""
    var emailAddress: String = ""
    var techID: String = ""
    var additionalCompanies: String = ""
    
    private enum DataKeys: String, CodingKey {
        case userID = "userID"
        case clientID = "clientID"
        case Name = "Name"
        case lastName = "lastName"
        case emailAddress = "emailAddress"
        case techID = "techID"
        case additionalCompanies = "additionalCompanies"
    }
    
    private enum DataResponseKeys: String, CodingKey {
        case data
    }
    
    init(from decoder: Decoder) throws {
        if let dataResponseContainer = try? decoder.container(keyedBy: DataResponseKeys.self) {
            if let dataContainer = try? dataResponseContainer.nestedContainer(keyedBy: DataKeys.self, forKey: .data) {
                self.userID = try dataContainer.decode(String.self, forKey: .userID)
                self.clientID = try dataContainer.decode(String.self, forKey: .clientID)
                self.Name = try dataContainer.decode(String.self, forKey: .Name)
                self.lastName = try dataContainer.decode(String.self, forKey: .lastName)
                self.emailAddress = try dataContainer.decode(String.self, forKey: .emailAddress)
                self.techID = try dataContainer.decode(String.self, forKey: .techID)
                self.additionalCompanies = try dataContainer.decode(String.self, forKey: .additionalCompanies)
            }
        }
    }
}

Solution

  • Try the following data model

    let userInformation = try? JSONDecoder().decode(UserInformation.self, from: jsonData)
    
    import Foundation
    
    // MARK: - UserInformation
    struct UserInformation: Codable {
        let data: DataClass
    }
    
    // MARK: - DataClass
    struct DataClass: Codable {
        let userID, techID, clientID, name: String
        let lastName, emailAddress, additionalCompanies: String
        let clientDetails: ClientDetails
    
        enum CodingKeys: String, CodingKey {
            case userID, techID, clientID
            case name = "Name"
            case lastName, emailAddress, additionalCompanies, clientDetails
        }
    }
    
    // MARK: - ClientDetails
    struct ClientDetails: Codable {
        let clientID, firstName, lastName, email: String
        let customerName, businessAddress, businessCity, businessState: String
        let businessZip, businessCountry, businessPhone: String
        let businessFax: String? // null value
        let mobilePhone, contactName, contactTitle, contactPhone: String
        let contactEmail, lastUpdated: String
    
        enum CodingKeys: String, CodingKey {
            case clientID
            case firstName = "FirstName"
            case lastName = "LastName"
            case email
            case customerName = "CustomerName"
            case businessAddress = "BusinessAddress"
            case businessCity = "BusinessCity"
            case businessState = "BusinessState"
            case businessZip = "BusinessZip"
            case businessCountry = "BusinessCountry"
            case businessPhone = "BusinessPhone"
            case businessFax = "BusinessFax"
            case mobilePhone = "MobilePhone"
            case contactName = "ContactName"
            case contactTitle = "ContactTitle"
            case contactPhone = "ContactPhone"
            case contactEmail = "ContactEmail"
            case lastUpdated
        }
    }
    

    For reference please follow documentation from
    encoding_and_decoding_custom_types Apple documentation

    raywenderlich encoding-and-decoding-in-swift