8

HI i am new to swift any idea . How to initialize a struct from a json object . i could not figure out how can i do it .

{ "user": { "name": "cruskuaka", "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" }

Here all struct :

struct Contact  {
    let user : User
    let address : Address
    let deliveryInstruction : String
    let deliveryMethod : String
    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["address"] as? [String:Any] ?? [:])
    }
  }

struct User {
    let name : String
    let email : String
    let phoneNo : String
    init(dictionary : [String:Any] ) {
        self.name = dictionary["name"] as? String ?? ""
        self.email = dictionary["email"] as? String ?? ""
        self.phoneNo = dictionary["phoneNo"] as? String ?? ""
    }
}

struct Address  {
    let city : City
    let town : Town
    let addressId : String
    let fullAddress : String
    let house : String
    let street: String
    init(dictionary : [String:Any] ) {
        self.addressId = dictionary["address_id"] as? String ?? ""
        self.fullAddress = dictionary["full_address"] as? String ?? ""
        self.house = dictionary["house"] as? String ?? ""
        self.street = dictionary["street"] as? String ?? ""
        self.city = City(dictionary: dictionary["address"] as? [String:Any] ?? [:])
        self.town = Town(dictionary: dictionary["address"] as? [String:Any] ?? [:])  
    }
}

struct City {
    let cityId : String
    let cityName : String
    init(dictionary : [String:Any] ) {
        self.cityId = dictionary["city_id"] as? String ?? ""
        self.cityName = dictionary["city_name"] as? String ?? ""
    }
}

struct Town {
    let townId : String
    let townName : String
    init(dictionary : [String:Any]) {
        self.townId = dictionary["town_id"] as? String ?? ""
        self.townName = dictionary["town_name"] as? String ?? ""
    }
} 
10
  • jsonFormatString name for a Swift dictionary it is very misleadung. change it to let contact: [String: Any] = Commented Mar 29, 2017 at 14:15
  • @LeoDabus thanks .. .. can you help me to solve please . any way you doing it or you flowing Commented Mar 29, 2017 at 14:17
  • try urlRequest.httpMethod = "POST" urlRequest.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") urlRequest.httpBody = jsonData Commented Mar 29, 2017 at 14:24
  • @LeoDabus not working same result Commented Mar 29, 2017 at 14:29
  • 1
    @LeoDabus my code working fine problem was server end .. thank you so much .. I wish you my best Commented Mar 30, 2017 at 7:01

1 Answer 1

11

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
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.