M A Russel
M A Russel

Reputation: 1557

What is the best way to convert JSON into swift object, Checking keys and values both

Ok I thought its not a major issue but I am wrong. Currently I am working on a project where I get huge chunk of JSON return. I am fetching those and making my model. Now in my model I am checking if there any value is nil by guard statement. Here is a sample of my model:

import Foundation
import SwiftyJSON

class profileModel {

var _phone_no: String?
var _full_name: String?
var _image: String?
var _email: String?
var _profile_pic: String?
var _rating: Int?
var _dob: String?
var _gender: String?
var _firstName: String?
var _lastName: String?


required init?(phone_no: String, full_name: String, image: String, email: String, profile_pic: String, rating: Int, dob: String, gender: String, firstName: String, lastName: String) {
    self._phone_no = phone_no
    self._full_name = full_name
    self._image = image
    self._email = email
    self._profile_pic = profile_pic
    self._rating = rating
    self._dob = dob
    self._gender = gender
    self._firstName = firstName
    self._lastName = lastName
}

convenience init?(json: JSON){

  guard let phone_no = json["phone_no"].string,
        let full_name = json["full_name"].string,
        let image = json["profile_pic"].string,
        let email = json["email"].string,
        let profile_pic = json["profile_pic"].string,
        let rating = json["rating"].int,
        let dob = json["dob"].string,
        let gender = json["gender"].string,
        let firstName = json["first_name"].string,
        let lastName = json["last_name"].string else {
        print("Profile Detail Model Error")
        return nil
    }

    self.init(phone_no: phone_no, full_name: full_name, image: image, email: email, profile_pic: profile_pic, rating: rating, dob: dob, gender: gender, firstName: firstName, lastName: lastName)

  }

}

But how can I prevent crashes when any key is missing from JSON return? Seems like when I check both key & values the class got really really big, there must be some better way.

Upvotes: 1

Views: 220

Answers (2)

Lausbert
Lausbert

Reputation: 1590

You should have a look at the Codable protocol: The following Playground shows what happens, when you try to parse a Json, that is missing a particular key.

//: Playground - noun: a place where people can play

import Foundation

At first, we create our ProfileModel class and mock a related json.

class ProfileModel: Codable {

    //...
    var _firstName: String?
    var _lastName: String?

}

let profile = ProfileModel()
profile._firstName = "Hans"
profile._lastName = "Peter"
let json = try! JSONEncoder().encode(profile)

Parsing works as expected:

do {
    let profileAgain = try JSONDecoder().decode(ProfileModel.self, from: json)
    print(profileAgain._firstName) // "Optional("Hans")\n"
    print(profileAgain._lastName) // "Optional("Peter")\n"
} catch {
    print("something went wrong")
}

So what happens, when we add another property to our class (_phone_no), that is not included in our Json? Nothing really changes, if this new property is optional:

class AnotherProfileModel: Codable {

    //...
    var _firstName: String?
    var _lastName: String?
    var _phone_no: Int?
}

do {
    let anotherProfile = try JSONDecoder().decode(AnotherProfileModel.self, from: json)
    print(anotherProfile._firstName) // "Optional("Hans")\n"
    print(anotherProfile._lastName) // "Optional("Peter")\n"
    print(anotherProfile._phone_no) // "nil\n"
} catch {
    print("something went wrong")
}

But if this property is not an optional, the decoder will throw an error:

class AndYetAnotherProfileModel: Codable {

    //...
    var _firstName: String?
    var _lastName: String?
    var _phone_no: Int
}

do {
    let andYetAnotherProfileModel = try JSONDecoder().decode(AndYetAnotherProfileModel.self, from: json)
} catch {
    print("something went wrong") // "something went wrong\n"
}

I hope this working example will help you, to get a better understanding of the Codable protocol :)

Upvotes: 1

aguilarpgc
aguilarpgc

Reputation: 1203

Making properties optionals is a good approach, however you can take advantage of the new Codable from Swift 4 where you can parse JSON to any data model that conformance to Codable.

In your case you can write the model like this:

class ProfileModel: Codable {
    var phone_no: String?
    var full_name: String?
    var profile_pic: String?
    var email: String?
//    var profile_pic: String?
    var rating: String?
    var dob: String?
    var gender: String?
    var first_name: String?
    var last_name: String?
}

And when you need to decode from the server use:

let profile = try JSONDecoder().decode(ProfileModel.self, from: json1)

If you get an array of "Profile" just change the above line to:

let profiles = try JSONDecoder().decode([ProfileModel].self, from: json1)

There is no need to use the library SwiftyJSON any more.

Upvotes: 2

Related Questions