Home Simplify JSON Decoding in iOS with this Neat Little Trick
Post
Cancel

Simplify JSON Decoding in iOS with this Neat Little Trick

Introduction

In iOS development, working with JSON data is a common task. To simplify the process of decoding JSON into Swift objects, Apple introduced the Decodable protocol. Decodable is a powerful protocol that automatically maps JSON data to Swift types, making JSON decoding straightforward. However, there are scenarios where the JSON structure may not precisely match the desired Swift type, leading to decoding failures. In this article, we’ll explore how to gracefully handle such edge cases using a neat DecodableData extension.

Handling JSON Mismatch

Consider a scenario where we have JSON representing user data like this:

1
2
3
4
5
6
7
{
    "user_data": {
        "full_name": "General Iroh",
        "country": "Fire Nation",
        "occupation": "Professional Tea Enjoyer"
    }
}

However, we want to map this JSON to a Swift type that looks like this:

1
2
3
4
5
6
struct User: Decodable {
    var name: String
    var country: String
    var occupation: String
    var hobbies: [String]
}

The problem here is that we have an additional property called "hobbies" that doesn’t exist in the incoming JSON, and the "name" property doesn’t match the JSON key "full_name". Fortunately, there is a clean extension we can improvise to handle this mismatch gracefully.

The DecodableData Extension

To address the JSON-Swift type mismatch, we can define an extension called DecodableData for the User struct. Let’s take a look at the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct User {
    let name: String
    let country: String
    let occupation: String
    var hobbies: [String]
}

extension User {
    struct DecodableData: Decodable {
        let fullName: String
        let country: String
        let occupation: String
        
        var user: User {
            return User(name: fullName, country: country, occupation: occupation)
        }
    }
}

The DecodableData extension provides a solution for decoding the user JSON data. It includes properties that match the JSON structure, such as “fullName”, “country”, and “occupation.” Additionally, it includes a computed property called "user", which returns an instance of the original User struct based on the decoded data. By mapping the properties correctly in the DecodableData struct, we can bridge the gap between the JSON structure and our desired Swift type.

Using DecodableData

To make use of DecodableData in a decoder, follow these steps:

1
2
3
4
5
6
7
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

// Decode an instance of User.DecodableData instead of User directly,
// then convert it to a User instance.
let decodableData = try decoder.decode(User.DecodableData.self, from: data)
let user = decodableData.user

In the above code, we use the "convertFromSnakeCase" key decoding strategy to automatically convert the "full_name" snake_case key from the JSON data into its "fullName" camelCase equivalent, matching our Swift code.

Conclusion

As you can see, the DecodableData extension proves invaluable when dealing with JSON-Swift type mismatches. By utilizing this approach, we can decode or encode specific types even when the original type has a completely different structure than the JSON data. This technique allows us to bridge the gap between serialized data and our Swift code, while still leveraging the power of auto mapping that the Decodable feature provides us.

This post is licensed under CC BY 4.0 by the author.