Decoding Dynamic JSON with Swift Codable

Handle dynamic server responses working with Swift’s sound type system

Soumya Mahunt
ITNEXT

--

Providing dynamic content in your app requires communicating with an external service to fetch data. The most common format that is used nowadays is JSON and the common way of handling JSON data in swift is to convert it to swift types with help of Codable. In the majority of the use cases, the data can be represented with a concrete swift type having a static list of properties.

In other cases, when the content in your app is highly dynamic with a wide range of scope, the response data may vary between a wide range of formats. Some common examples are:

  • Building dynamic server-driven UI with CMS.
  • Display a wide range of products on an e-commerce website.
  • Dynamic content feed in a social network.

In this post, we will see the typical way of handling decoding these kinds of responses, and how DynamicCodableKit makes it seamless to handle dynamic responses by building on top of Swift’s Codable.

Current State

Following is Amazon’s suggestion response data when a user types any text in the search box, in this scenario “microwave”:

The suggestions property contains a list of dynamic results for the search, with each result containing a field type representing the actual type of result. In the above response, KEYWORD and WIDGET type results are received, with each type containing some additional metadata, which in turn is used to present the additional option to users like this:

One approach is to use a single struct with optional fields. Although this approach seems simpler, with this approach you can use default synthesized Codable implementation. The drawback of this approach is you have to pollute your business logic with data validation code and for cases where the same property can have a different type you are out of luck with this approach.

Another and much-preferred approach is to use an enum with associated values. It addresses the shortcoming of the previous approach of removing nil property requirements and solving property type conflicts by adding separate cases for each type to decode. Custom init(from:) implementation for the enum can be provided that will decode the underlying case/type.

In our example, we can check the type field for each object and decode the suggestion accordingly:

Using DynamicCodableKit

While the above approach works for responses with a smaller level of dynamism, it becomes much harder to maintain all this boilerplate. The major drawback is you have to do some additional work to access common properties across all cases and with enums, you have to update all the switch cases each time you add a new type.

This is where DynamicCodableKit shines by using generic property wrappers instead. It handles dynamic decoding based on DynamicDecodingContext which is a type erasure that decodes a specific DynamicDecodable type and casts it to its generic type. You can customize DynamicDecodingContext in different scenarios to dynamically decode multiple types. DynamicCodableKit also provides multiple configurations for collection decoding, to customize what happens when invalid data is encountered, i.e. you can choose to decode only valid data while ignoring invalid data instead of just throwing an error.

For our current example, we can create a Suggestion protocol type that will provide access to common properties and methods for underlying actual Suggestions, i.e. KeywordSuggestion, WidgetSuggestion. SuggestionType and SuggestionTypeCodingKey can implement DynamicDecodingContextIdentifierKey and DynamicDecodingContextIdentifierCodingKey respectively to decode the actual types. Compared to the previous approach, adding new types only requires changing SuggestionType's associatedContext implementation:

NOTE: By default, DynamicDecodable only supports down casting, if you want to cast to an unrelated type, you have to provide a custom implementation.

Conclusion

Besides the above example, DynamicCodableKit provides several property wrappers and associated protocols to handle various dynamic decoding scenarios, all of them explained in detail in the documentation making dynamic decoding a lot simpler to use with minimal boiler-plate.

--

--

Writer for

Senior Software Engineer at MoEngage | System architecture enthusiast | Ex Tataneu