'Swift JSON decoder different types

I have these two JSON objects

[ 
{"name": "Popular Movies", 
"description": "Basic movie description", 
"type": "movies", 
"items": [ { "id": 15, "name": "Sample movie", "movieSPT": ""}]
},
{"name": "Popular TV Shows", 
"description": "Basic shows description", 
"type": "tvshows", 
"items": [ { "id": 15, "name": "Sample show", "showSPT": ""}]
}
]

Then i created two decodable structs, for shows and for movies:

struct Movie: Decodable {
    let id: Int
    let name: String
    let movieSPT: String
}

struct TVShow: Decodable {
    let id: Int
    let name: String
    let showSPT: String
}

So, when i create an object for main response, what is the best way to create an items array depends on type value? I know i can create both showSPT and movieSPT with optional properties for some unique struct, but is it the right way? Also, if these two models will have a lot of properties, the combine struct will be too big.

struct Blocks : Decodable {
    let name: String
    let description: String
    let type: String
    let items: [Movie] or [Show] based on type????
}


Solution 1:[1]

There are a couple of solutions. One of them is an enum with associated values

let jsonString = """
[{"name": "Popular Movies", "description": "Basic movie description", "type": "movies",
    "items": [ { "id": 15, "name": "Sample movie", "movieSPT": ""}]
},
{"name": "Popular TV Shows", "description": "Basic shows description", "type": "tvshows",
"items": [ { "id": 15, "name": "Sample show", "showSPT": ""}]
}
]
"""
let data = Data(jsonString.utf8)


struct Movie : Decodable {
    let id: Int
    let name, movieSPT: String
}

struct TVShow : Decodable {
    let id: Int
    let name, showSPT: String
}

enum MediaType {
    case movie([Movie]), tvShow([TVShow])
}

struct Media : Decodable {
    let name : String
    let description : String
    let items : MediaType
    
    private enum CodingKeys : String, CodingKey { case name, description, type, items }
    
    init(from decoder : Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.description = try container.decode(String.self, forKey: .description)
        let type = try container.decode(String.self, forKey: .type)
        if type == "movies" {
            let movieData = try container.decode([Movie].self, forKey: .items)
            items = .movie(movieData)
        } else { // add better error handling
            let showData = try container.decode([TVShow].self, forKey: .items)
            items = .tvShow(showData)
        }
        
    }
}

do {
    let result = try JSONDecoder().decode([Media].self, from: data)
    print(result)
} catch {
    print(error)
}

Solution 2:[2]

You can decode the JSON, then filter out the decoded array by type.

    import Foundation

    let jsonData = """
    [
    {"name": "Popular Movies",
    "description": "Basic movie description",
    "type": "movies",
    "items": [ { "id": 15, "name": "Sample movie", "movieSPT": ""}]
    },
    {"name": "Popular TV Shows",
    "description": "Basic shows description",
    "type": "tvshows",
    "items": [ { "id": 15, "name": "Sample show", "showSPT": ""}]
    }
    ]
    """.data(using: .utf8)!


    // MARK: - MediaElement
struct MediaElement: Codable {
    let name, mediaDescription, type: String
    let items: [Item]

    enum CodingKeys: String, CodingKey {
        case name
        case mediaDescription = "description"
        case type, items
    }
}

// MARK: - Item
struct Item: Codable {
    let id: Int
    let name: String
    let movieSPT, showSPT: String?
}

typealias Media = [MediaElement]


let decoder = JSONDecoder()

let media = try? decoder.decode(Media.self, from: jsonData)

let moviesMediaArray = media?.filter {$0.type == "movies"}
let tvshowsMediaArray = media?.filter {$0.type == "tvshows"}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2