简体   繁体   中英

Error handling parsing JSON in Swift

I'm using Alamofire and am parsing the returned JSON into an object as shown below:

final class User: NSObject, ResponseObjectSerializable {
    var id: Int
    var facebookUID: String?
    var email: String
    var firstName: String
    var lastName: String
    var phone: String?
    var position: String?
    var timeCreated: CVDate

    init?(response: NSHTTPURLResponse, var representation: AnyObject) {
        if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
            representation = dataRepresentation
        }

        if let id = representation.valueForKeyPath("id") as? Int {
            self.id = id
        } else {
            self.id = 0
        }

        if let facebookUID = representation.valueForKeyPath("facebook_UID") as? String {
            self.facebookUID = facebookUID
        }

        if let email = representation.valueForKeyPath("email") as? String {
            self.email = email
        } else {
            self.email = ""
        }

        if let firstName = representation.valueForKeyPath("first_name") as? String {
            self.firstName = firstName
        } else {
            self.firstName = ""
        }

        if let lastName = representation.valueForKeyPath("last_name") as? String {
            self.lastName = lastName
        } else {
            self.lastName = ""
        }

        if let phone = representation.valueForKeyPath("phone") as? String {
            self.phone = phone
        }

        if let position = representation.valueForKeyPath("position_name") as? String {
            self.position = position
        }

        if let timeCreated = representation.valueForKeyPath("time_created") as? String {
            let formatter = NSDateFormatter()
            formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
            if let date = formatter.dateFromString(timeCreated) {
                self.timeCreated = CVDate(date: date)
            } else {
                self.timeCreated = CVDate(date: NSDate())
            }
        } else {
            self.timeCreated = CVDate(date: NSDate())
        }
    }
}

My question is, is this style the best way to decode JSON and set the non-optional instance variables? For example, in this statement:

if let id = representation.valueForKeyPath("id") as? Int {
    self.id = id
}

I am required by the compiler to add an else clause and set the id to something otherwise xCode throws an error saying: self.id is not initialized at implicitly generated super.init call .

But at the same time, intializing self.id with a value of 0 is wrong and doesn't help me at all.

You could use ?? to provide default values like this:

self.id = (representation.valueForKeyPath("id") as? Int) ?? 0

But at the same time, intializing self.id with a value of 0 is wrong and doesn't help me at all.

If having a default value for self.id feels wrong, then you should make this property an Optional. That way you wouldn't have to add an else clause:

final class User: NSObject, ResponseObjectSerializable {
    var id: Int?
    var facebookUID: String?
    var email: String
    var firstName: String
    var lastName: String
    var phone: String?
    var position: String?
    var timeCreated: CVDate

    init?(response: NSHTTPURLResponse, var representation: AnyObject) {
        if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
            representation = dataRepresentation
        }

        if let id = representation.valueForKeyPath("id") as? Int {
            self.id = id
        }

        ...

Update

You said in the comments:

I always need to have an id for the user object though.

If you have to have this id property then the question is moot, you just have to do

let id = representation.valueForKeyPath("id") as! Int 

and guarantee earlier that this value will exist.

Because if your object needs an ID, then you can't initialize it anyway if this value doesn't exist and if you don't want a default value.

While the ResponseObjectSerializable code is a great example from the Alamofire project, it's really a better idea to use a dedicated JSON parsing library that has actual error states. This is far better than using optionals to represent error states, or having to provide a default value for every field just in case the response isn't correctly formed.

Although it has a bit of learning curve, I prefer to use Argo for my JSON parsing. Once you get the hang of it it makes JSON parsing practically bulletproof. Better yet, it's easy to integrate with Alamofire, especially version 3 that was released today.

To address your concern about not having an ID being an error condition, you could use a failable initializer. I did that in a recent project. Looks something like this:

let id: Int!

init? (inputJson: NSDictionary) {

    if let id = inputJson["id"] as? Int {
        self.id = id
    } else {
        // if we are initing from JSON, there MUST be an id
        id = nil
        cry(inputJson) // this logs the error
        return nil
    }
}

Of course, this means your code will need to accept that the initialization of your entire object may fail ..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM