How to parse JSON in Swift? (Encodable, Decodable, and handling Null values) - Part Two

Note: If you are a beginner and you find it difficult to understand things written in this article. Feel free to ask questions on my Reddit page or Twitter.

If you haven’t read my first article about Understanding Codable - Part One, please click on the link.

Remember, you can do it. ; )


Handling NULL

Let’s assume our email property has a null value, or maybe our email property itself in the JSON is missing on some occasion.

Our app is crashing. What will we do now?

// JSON with null value.
let jsonString = """
{
    "first_name": "Karan",
    "lastName": "Pal",
    "email": null,
    "id": 101
}
"""
 // JSON. with email key missing.
let jsonString = """
{
    "first_name": "Karan",
    "lastName": "Pal",
    "id": 101
}
"""

If you have been following along my Understanding Codable article, then in the jsonString variable, replace the value of the email key with null, as shown in the JSON above.

Many of us deal with null values in the JSON or a Missing Key, especially when you are working with NoSQL Database.

In Swift, it’s the same thing. Our everyday code requires the handling of nil values, and how do we do that?

var email: String?

So, what’s new”?”

🤔

Optional, wasn’t it quick? Just making your property Optional is going to solve these problems.

BUT, What If I don’t want to handle optional?

Handling Optional for the rest of the code can be frustrating for some. So PAL, what should we do?

Don’t worry, here comes the Decodable protocol.

Decodable

Decodable provides us with an initializer to decode values. We generally don’t implement the initializer function until we want to do some operations before storing the values into the property.

So, let’s see how we can handle the nil value or a/the missing key.

Modify the Contact struct to match the code below. You can get rid of the question symbol we added to the email property as it is no more required.

struct Contact: Codable {
    var firstName: String
    var lastName: String
    var email: String
    var id: Int
    
    enum CodingKeys: String, CodingKey { // Our Saviour
        case firstName = "first_name"
        case lastName
        case email
        case id
    }
    
    // The Initializer function from Decodable
    init(from decoder: Decoder) throws {
        // 1 - Container
        let values = try decoder.container(keyedBy: CodingKeys.self)
        
        // 2 - Normal Decoding
        firstName = try values.decode(String.self, forKey: .firstName)
        lastName = try values.decode(String.self, forKey: .lastName)
        id = try values.decode(Int.self, forKey: .id)
        
        // 3 - Conditional Decoding
        if var email =  try values.decodeIfPresent(String.self, forKey: .email) {
            self.email = email
        }else {
            self.email = "N/A"
        }
    }
}

We have added the initializer function declared in the Decodable protocol. The explanation of the operations done in the function is below. (Point numbers below are referenced with the comments mentioned in the initializer function)

  1. We requested all the values from the container to store it in the variable values with the help of enum CodingKeys declared in the struct.

  2. Here, the values are retrieved with the help of the key and stored into the mentioned property.

  3. Here, we are decoding the value with the help of the conditional statement, checking if the key is present, or it has a value before retrieving with the help of decodeIfPresent(String.self, forKey: .email)  function. If the condition fails, a default value is requested to be stored in the property.

That’s how we do manual decoding. Bookmark this page; you will be decoding a lot of values now with Codable.

We are done with decoding and populating the data. Next, we need our data back to post it on our server. The most common form of posting the data on the server is through the Dictionary. So, how should we convert our data to the Dictionary or String type.

Don’t worry. I have covered it all.

Encodable

I’ll give you an extension solution for all your future Encoding problems.

extension Encodable {
    
    func encode() -> [String: Any]? {
        do {
            let data = try JSONEncoder().encode(self)
            let jsonDict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
            return jsonDict
        }catch {
            print(error.localizedDescription)
            return nil
        }
    }
    
}

Add this beautiful piece of code in all your projects.

It’s just the reverse of decoding. I’ll leave this code to your self-understanding, if you face any difficulty, comment below. After all, I am here for you.

How do we use this function?

let jsonContact = contact.encode()
print(jsonContact)

That’s how.

But, do we want to push the “N/A” stored in the email property on the server?

Nope!

Just like we were able to customize the way of decoding the JSON, we can also customize the way of encoding the Codable.

Add this code mentioned below in your struct.

// The encoding function from Encodable
func encode(to encoder: Encoder) throws {
    
    var container = encoder.container(keyedBy: CodingKeys.self)
    
    try container.encode(firstName, forKey: .firstName)
    try container.encode(lastName, forKey: .lastName)
    
    if self.email == "N/A" {
        try container.encode("", forKey: .email)
    }else {
        try container.encodeIfPresent(email, forKey: .email)
    }
    try container.encode(id, forKey: .id)
    
}

First, in the function, we are retrieving the container with the help of CodingKeys (the enum declared in our struct).

Next, we have stored the firstName and lastName property in the container to encode.

Next, we have used a conditional statement to determine the value which will be stored for the key email in our container.

Finally, we have stored the id, and that is all.

You can customize the function as per your needs. I think Codable is very easy to use and very powerful if used in the right way.

Don’t worry. I won’t act like asking a question again and then making you read another section 😅 .

And we are done here, I’ll see you again in my next article but for that, please subscribe to my website to stay updated.

If you have any suggestions or questions, Feel free to connect me on Twitter. 😉

Download the final version of the playground from here.

How to Format Data, and improve UX in Swift? - Part One

How to parse JSON in Swift? (Codable: Parsing JSON Basics) - Part One