edit/update: Swift 4 or later you can use Codable Protocol:
struct Root: Codable {
let user: User
let address: Address
let deliveryInstruction, deliveryMethod: String
}
struct Address: Codable {
let house, street, addressId, fullAddress: String
let town: Town
let city: City
}
struct City: Codable {
let cityId, cityName: String
}
struct Town: Codable {
let townId, townName: String
}
struct User: Codable {
let name, email, phoneNo: String
}
extension Decodable {
init(data: Data, using decoder: JSONDecoder = .init()) throws {
self = try decoder.decode(Self.self, from: data)
}
init(json: String, using decoder: JSONDecoder = .init()) throws {
try self.init(data: Data(json.utf8), using: decoder)
}
}
Just don't forget to set the JSONDecoder property keyDecodingStrategy
to .convertFromSnakeCase
:
let json = """
{"user": {"name": "crst","email": "[email protected]","phoneNo":"018833455"},"address": {"house": "100","street": "B","town":{"town_id": "1","town_name": "Galway city center"},"city":{"city_id": "10","city_name": "Galway"},"address_id":"200", "full_address":"100, B, Galway city center,Galway" },"delivery_instruction": "no call","delivery_method": "1" }
"""
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let root = try decoder.decode(Root.self, from: Data(json.utf8))
print(root)
} catch {
print(error)
}
or simply:
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let root = try Root(json: json, using: decoder) // or Root(data: data, using: decoder)
print(root)
} catch {
print(error)
}
This will print
Root(user: __lldb_expr_112.User(name: "cruskuaka", email:
"[email protected]", phoneNo: "018833455"), address:
__lldb_expr_112.Address(house: "100", street: "B", addressId: "200", fullAddress: "100, B, Galway city center,Galway", town:
__lldb_expr_112.Town(townId: "1", townName: "Galway city center"), city: __lldb_expr_112.City(cityId: "10", cityName: "Galway")),
deliveryInstruction: "no call", deliveryMethod: "1")
Original Answer (before Codable protocol)
Swift 3
You have more than one error in your code, but you are in the right path. You are using the wrong key when initializing your user, city and town structs. I have also created two more initializers so you can initialize your struct with a dictionary, the json string or just its data:
struct Contact: CustomStringConvertible {
let user: User
let address: Address
let deliveryInstruction: String
let deliveryMethod: String
// customize the description to your needs
var description: String { return "(user.name) (deliveryInstruction) (deliveryMethod)" }
init(dictionary: [String: Any]) {
self.deliveryInstruction = dictionary["delivery_instruction"] as? String ?? ""
self.deliveryMethod = dictionary["delivery_method"] as? String ?? ""
self.address = Address(dictionary: dictionary["address"] as? [String: Any] ?? [:])
self.user = User(dictionary: dictionary["user"] as? [String: Any] ?? [:])
}
init?(data: Data) {
guard let json = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] else { return nil }
self.init(dictionary: json)
}
init?(json: String) {
self.init(data: Data(json.utf8))
}
}
struct User: CustomStringConvertible {
let name: String
let email: String
let phone: String
let description: String
init(dictionary: [String: Any]) {
self.name = dictionary["name"] as? String ?? ""
self.email = dictionary["email"] as? String ?? ""
self.phone = dictionary["phoneNo"] as? String ?? ""
self.description = "name: (name) - email: (email) - phone: (phone)"
}
}
struct Address: CustomStringConvertible {
let id: String
let street: String
let house: String
let city: City
let town: Town
let description: String
init(dictionary: [String: Any] ) {
self.id = dictionary["address_id"] as? String ?? ""
self.description = dictionary["full_address"] as? String ?? ""
self.house = dictionary["house"] as? String ?? ""
self.street = dictionary["street"] as? String ?? ""
self.city = City(dictionary: dictionary["city"] as? [String: Any] ?? [:])
self.town = Town(dictionary: dictionary["town"] as? [String: Any] ?? [:])
}
}
struct City: CustomStringConvertible {
let id: String
let name: String
// customize the description to your needs
var description: String { return name }
init(dictionary: [String: Any] ) {
self.id = dictionary["city_id"] as? String ?? ""
self.name = dictionary["city_name"] as? String ?? ""
}
}
struct Town: CustomStringConvertible {
let id: String
let name: String
// customize the description to your needs
var description: String { return name }
init(dictionary: [String: Any]) {
self.id = dictionary["town_id"] as? String ?? ""
self.name = dictionary["town_name"] as? String ?? ""
}
}
Testing the initialization from JSON:
let contact = Contact(json: json) // crst no call 1
contact // crst no call 1
contact?.user // name: crst - email: [email protected] - phone: 018833455
contact?.user.name // "crst"
contact?.user.email // "[email protected]"
contact?.user.phone // "018833455"
contact?.address // 100, B, Galway city center,Galway
contact?.address.id // 200
contact?.address.street // B
contact?.address.town // Galway city center
contact?.address.city // Galway
contact?.deliveryInstruction // "no call"
contact?.deliveryMethod // 1