Search code examples
iosswiftxcodealamofiremoya

Casting to object with Codable


All my JSON responses follow the same structure:

"success": <http code>,
"data": [

]

Where the data sent back can vary. Sometimes it can contain Users, sometimes Comments, etc. So I want to create a Codable struct that is flexible to handle the various types of objects being sent back in the data array.

Here is my current struct:

struct BasicResponse: Codable {
    let success: Int
    let data: [User]
}

As you can see, it currently only handles User data being sent back.

Then, I read the JSON data like this (through Alamofire/Moya):

var users = [User]()

let results = try JSONDecoder().decode(BasicResponse.self, from: response.data)

self.users.append(contentsOf: results.data)

How can I change my struct file to be more flexible, and how would I then cast the JSON response to the desired object?


Solution

  • So, without going through a lot of design cycles and straight off the top my head, I'd consider trying Swift's generic support, for example...

    struct BasicResponse<DataType>: Codable where DataType: Codable {
        let success: Int
        let data: [DataType]
    }
    

    Then you just need to define the implementation of DataTypes you want to use

    struct User: Codable {
        var name: String
    }
    

    And decode it...

    let decoder = JSONDecoder()
    let response = try decoder.decode(BasicResponse<User>.self, from: data)
    print(response.data[0].name)
    

    Now, I just threw this into a Playground and tested it with some basic data...

    struct User: Codable {
        var name: String
    }
    
    struct BasicResponse<T>: Codable where T: Codable {
        let success: Int
        let data: [T]
    }
    
    let data = "{\"success\": 200, \"data\": [ { \"name\":\"hello\" }]}".data(using: .utf8)!
    
    let decoder = JSONDecoder()
    do {
        let response = try decoder.decode(BasicResponse<User>.self, from: data)
        response.data[0].name
    } catch let error {
        print(error)
    }
    

    You might need to "massage" the design to better meet your needs, but it might give you a place to start