Search code examples
iosswiftswift3codable

Managing Dynamic Keys in response through Codable Protocol


I need to make the codable model for the dynamic keys of dictionary coming from response below is the response I'm getting.

{ 
"data" : [
             {
               "desc1" : null,
               "file1" : "uploads\/posts\/Aug-2021\/1629271422310452767"
             },
             {
               "desc2" : "hello",
               "file2" : "uploads\/posts\/Aug-2021\/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"
             }
         ],
"status" : "success"
}

This desc1 and file1 is dynamic till like file1, file2 and so on, I need to have codable model for that below is my model that is not supportive.

struct ListModel: Codable {
    public var data: [data]?
}
struct data: Codable {
   let file : String?
   let desc : String?
}

Anything support by codable protocol for that. Thanks in Advance.


Solution

  • You need a custom initializer. Of course this will only work if your json will always come formatted as described:

    struct File {
        var file: String? = ""
        var desc: String? = ""
    }
    
    struct Response {
        let files: [File]
        let status: String
        enum CodingKeys: String, CodingKey {
            case files = "data", status
        }
    }
    
    extension Response: Decodable {
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.status = try container.decode(String.self, forKey: .status)
            let elements = try container.decode([[String: String?]].self, forKey: .files)
            self.files = elements.reduce(into: []) {
                var file = File()
                for (key, value) in $1 {
                    if key.hasPrefix("file") {
                        file.file = value
                    } else if key.hasPrefix("desc") {
                        file.desc = value
                    }
                }
                $0.append(file)
            }
        }
    }
    

    Playground testing:

    let json = """
    {
        "data" : [
                     {
                       "desc1" : null,
                       "file1" : "uploads/posts/Aug-2021/1629271422310452767"
                     },
                     {
                       "desc2" : "hello",
                       "file2" : "uploads/posts/Aug-2021/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"
                     }
                 ],
        "status" : "success"
    }
    """
    

    do {
        let response = try JSONDecoder().decode(Response.self, from: Data(json.utf8))
        print(response)
    } catch {
        print(error)
    }
    

    This will print:

    Response(files: [File(file: Optional("uploads/posts/Aug-2021/1629271422310452767"), desc: nil), File(file: Optional("uploads/posts/Aug-2021/162927142279356160WhatsApp+Image+2021-07-02+at+12.09.14+PM.jpeg"), desc: Optional("hello"))], status: "success")