Swift JSONDecode decoding arrays fail if single element decoding fails


In Swift, working with JSON objects is very easy with the JSONDecoder class. It is always necessary to create model classes or structs with the Codable protocol. One single mistake leads you to fail to decode the complete JSON object. Let's explore some examples to understand when this failure might occur and how you can handle it in Swift.

What is the JSONDecoder class?

The JSONDecoder class is then used to parse the JSON data from the file into an instance of the given Type either a class or structure. The decode(_:from:) method is used to deserialize the JSON data, and the resulting Type object is then returned to access the properties of the Type object.

We are attempting to read the same JSON given above as an example using the Codable protocol. Before we are ready to decode the JSON data using the JSONDecoder() class, let's prepare the model class to be provided at the time of decoding.

Here is an example of creating this failure condition

Step 1 - Create a JSON file

In this step, we will create a sample JSON and save this as a ".json" file in your Xcode project. Here is the JSON that we will use in this example −

[
   {
      "name": "Tom Cruise",
      "age": 56,
      "bornAt": "Syracuse, NY",
      "birthdate": "July 3, 1962",
      "photo": "https://jsonformatter.org/img/tom-cruise.jpg",
      "topMovie": "Mission Impossible"
   },
   {
      "name": "Robert Downey Jr.",
      "age": 53,
      "bornAt": "New York City, NY",
      "birthdate": "April 4, 1965",
      "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg"
   }
]

We saved this JSON file by naming it "sample.json" in the project directory.

Step 2 - Create a Model Structure

This step involves creating a struct named "Actor" and conforming to the Decodable protocol. Here is the code −

import Foundation
struct Actor: Decodable {
   let name: String
   let age: Int
   let bornAt: String
   let birthdate: String
   let photo: String
   let topMovie: String
}

Step 3 - Decode JSON into an Actor struct

In this step, we will decode the JSON file and save data into the Actor model. Here is the code −

import UIKit
class CodableController: UIViewController {
       
   override func viewDidLoad() {
      super.viewDidLoad()
      decodeJSON()
   }
   func decodeJSON() {
      do {
         // creating path from the main bundle and getting data object from the path
         if let bundlePath = Bundle.main.path(forResource: "sample", ofType: "json"),
         let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {
                         
            let actors = try JSONDecoder().decode([Actor].self, from: jsonData)
            print("Number of actors parsed: \(actors.count)")
         }
      } catch {
         print(error)
      }
   }
}

In the above example, we create a view controller named "CodableController" along with a method. In this method, we are reading the local JSON file from the main bundle. After that, we decode the JSON data using the JSONDecoder().decode() method. We passed the type "[Actor].self" to the decode method, which returns an array of actor objects if it is successfully decoded.

In this example, the json variable contains an array of two objects, where the second object is invalid (it is missing a required property). The JSONDecoder is used to decode each object and any errors that occur during decoding are caught and printed to the console. The successfully decoded objects are added to an array called actors. The output of the example would be −

keyNotFound(CodingKeys(stringValue: "topMovie", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 1", intValue: 1)], debugDescription: "No value associated with key CodingKeys(stringValue: "topMovie", intValue: nil) ("topMovie").", underlyingError: nil))

As you can see, the decoding process failed due to missing values in the JSON data. It is clear to say that the value of the key "topMovie" is missing.

Step 4 - Fix the error

In this step, we will try to fix the above error. According to the JSON file, the "topMovie" key is missing from the second object. If you think this value is not required to perform further action in the application, you can make it optional as like below −

import Foundation
struct Actor: Decodable {
   let name: String
   let age: Int
   let bornAt: String
   let birthdate: String
   let photo: String
   let topMovie: String?
}

Now, you can run the code again and you will see the output printed on the console −

Number of actors parsed: 2

The decoding process is successfully completed without failure, as you can see.

Conclusion

Swift provides the JSONDecoder class to encode and decode the JSON objects into Swift's model objects and vice versa. It's our responsibility to handle property declarations carefully, otherwise, it will cause you to generate errors.

Those variables can be made optional in the case of missing values in the JSON object. In view of the possibility that these values might not appear in the server response, it is intended to have them optional.

Using another decoding technique that decodes each object separately and discards any decoding errors is one way to correct this problem. This enables decoding to proceed even if one or more elements are unable to be decoded.

You can apply a custom decoding approach by calling the JSONDecoder instance's decode( :from:completionHandler:) method and passing it a closure that accepts a Result object as a parameter. Either an array of properly decoded items or an error is contained in the Result object.

Updated on: 11-Apr-2023

156 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements