Decoding dynamic JSON structure in swift 4





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







0















I have the following issue that I'm not sure how to handle.



My JSON response can look like this:



{ 
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}


Or like this:



{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}


So in short the data can be either a single objet or an Array. What i have is this:



struct ApiData: Decodable {
var data: DataObject?
var error: String?
}

struct DataObject: Decodable {
var userId: Int?

enum CodingKeys: String, CodingKey {
case userId = "id"
}
}


This works fine for the first use case, but it will fail once data turns into



var data: [DataObject?]



How do I make that dynamic without duplicating code?



Edit: This is how i decode the object as well



 func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(_ apiData: ApiData?) -> ()) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

session.dataTask(with: urlRequest) {
(data, response, error) in
guard let _ = response, let data = data else {return}

if let responseCode = response as? HTTPURLResponse {
print("Response has status code: (responseCode.statusCode)")
}

do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
completion(retreived)
} catch let decodeError as NSError {
print("Decoder error: (decodeError.localizedDescription)n")
return
}
}.resume()
}









share|improve this question

























  • And what does the API send in case of error != null?

    – vadian
    Jan 3 at 9:59











  • @vadian it sends a string, u can see that in the ApiData structure.

    – CodeGeass
    Jan 3 at 10:03











  • @RobertDresler I'm not sure i understand the question? With the help of the struct ApiData that conforms to decodable protocol

    – CodeGeass
    Jan 3 at 10:04











  • @RobertDresler i've edited the post to answer your question.

    – CodeGeass
    Jan 3 at 10:08











  • Use power of generic

    – SPatel
    Jan 3 at 10:11


















0















I have the following issue that I'm not sure how to handle.



My JSON response can look like this:



{ 
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}


Or like this:



{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}


So in short the data can be either a single objet or an Array. What i have is this:



struct ApiData: Decodable {
var data: DataObject?
var error: String?
}

struct DataObject: Decodable {
var userId: Int?

enum CodingKeys: String, CodingKey {
case userId = "id"
}
}


This works fine for the first use case, but it will fail once data turns into



var data: [DataObject?]



How do I make that dynamic without duplicating code?



Edit: This is how i decode the object as well



 func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(_ apiData: ApiData?) -> ()) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

session.dataTask(with: urlRequest) {
(data, response, error) in
guard let _ = response, let data = data else {return}

if let responseCode = response as? HTTPURLResponse {
print("Response has status code: (responseCode.statusCode)")
}

do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
completion(retreived)
} catch let decodeError as NSError {
print("Decoder error: (decodeError.localizedDescription)n")
return
}
}.resume()
}









share|improve this question

























  • And what does the API send in case of error != null?

    – vadian
    Jan 3 at 9:59











  • @vadian it sends a string, u can see that in the ApiData structure.

    – CodeGeass
    Jan 3 at 10:03











  • @RobertDresler I'm not sure i understand the question? With the help of the struct ApiData that conforms to decodable protocol

    – CodeGeass
    Jan 3 at 10:04











  • @RobertDresler i've edited the post to answer your question.

    – CodeGeass
    Jan 3 at 10:08











  • Use power of generic

    – SPatel
    Jan 3 at 10:11














0












0








0








I have the following issue that I'm not sure how to handle.



My JSON response can look like this:



{ 
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}


Or like this:



{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}


So in short the data can be either a single objet or an Array. What i have is this:



struct ApiData: Decodable {
var data: DataObject?
var error: String?
}

struct DataObject: Decodable {
var userId: Int?

enum CodingKeys: String, CodingKey {
case userId = "id"
}
}


This works fine for the first use case, but it will fail once data turns into



var data: [DataObject?]



How do I make that dynamic without duplicating code?



Edit: This is how i decode the object as well



 func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(_ apiData: ApiData?) -> ()) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

session.dataTask(with: urlRequest) {
(data, response, error) in
guard let _ = response, let data = data else {return}

if let responseCode = response as? HTTPURLResponse {
print("Response has status code: (responseCode.statusCode)")
}

do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
completion(retreived)
} catch let decodeError as NSError {
print("Decoder error: (decodeError.localizedDescription)n")
return
}
}.resume()
}









share|improve this question
















I have the following issue that I'm not sure how to handle.



My JSON response can look like this:



{ 
"data": {
"id": 7,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDY1MTU0NDMsImRhdGEiOiJ2bGFkVGVzdCIsImlhdCI6MTU0NjUwODI0M30.uwuPhlnchgBG4E8IvHvK4bB1Yj-TNDgmi7wUAiKmoVo"
},
"error": null
}


Or like this:



{
"data": [{
"id": 12
}, {
"id": 2
}, {
"id": 5
}, {
"id": 7
}],
"error": null
}


So in short the data can be either a single objet or an Array. What i have is this:



struct ApiData: Decodable {
var data: DataObject?
var error: String?
}

struct DataObject: Decodable {
var userId: Int?

enum CodingKeys: String, CodingKey {
case userId = "id"
}
}


This works fine for the first use case, but it will fail once data turns into



var data: [DataObject?]



How do I make that dynamic without duplicating code?



Edit: This is how i decode the object as well



 func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(_ apiData: ApiData?) -> ()) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

session.dataTask(with: urlRequest) {
(data, response, error) in
guard let _ = response, let data = data else {return}

if let responseCode = response as? HTTPURLResponse {
print("Response has status code: (responseCode.statusCode)")
}

do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data)
completion(retreived)
} catch let decodeError as NSError {
print("Decoder error: (decodeError.localizedDescription)n")
return
}
}.resume()
}






swift decodable






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 at 13:06









SPatel

1,84121430




1,84121430










asked Jan 3 at 9:54









CodeGeassCodeGeass

2141314




2141314













  • And what does the API send in case of error != null?

    – vadian
    Jan 3 at 9:59











  • @vadian it sends a string, u can see that in the ApiData structure.

    – CodeGeass
    Jan 3 at 10:03











  • @RobertDresler I'm not sure i understand the question? With the help of the struct ApiData that conforms to decodable protocol

    – CodeGeass
    Jan 3 at 10:04











  • @RobertDresler i've edited the post to answer your question.

    – CodeGeass
    Jan 3 at 10:08











  • Use power of generic

    – SPatel
    Jan 3 at 10:11



















  • And what does the API send in case of error != null?

    – vadian
    Jan 3 at 9:59











  • @vadian it sends a string, u can see that in the ApiData structure.

    – CodeGeass
    Jan 3 at 10:03











  • @RobertDresler I'm not sure i understand the question? With the help of the struct ApiData that conforms to decodable protocol

    – CodeGeass
    Jan 3 at 10:04











  • @RobertDresler i've edited the post to answer your question.

    – CodeGeass
    Jan 3 at 10:08











  • Use power of generic

    – SPatel
    Jan 3 at 10:11

















And what does the API send in case of error != null?

– vadian
Jan 3 at 9:59





And what does the API send in case of error != null?

– vadian
Jan 3 at 9:59













@vadian it sends a string, u can see that in the ApiData structure.

– CodeGeass
Jan 3 at 10:03





@vadian it sends a string, u can see that in the ApiData structure.

– CodeGeass
Jan 3 at 10:03













@RobertDresler I'm not sure i understand the question? With the help of the struct ApiData that conforms to decodable protocol

– CodeGeass
Jan 3 at 10:04





@RobertDresler I'm not sure i understand the question? With the help of the struct ApiData that conforms to decodable protocol

– CodeGeass
Jan 3 at 10:04













@RobertDresler i've edited the post to answer your question.

– CodeGeass
Jan 3 at 10:08





@RobertDresler i've edited the post to answer your question.

– CodeGeass
Jan 3 at 10:08













Use power of generic

– SPatel
Jan 3 at 10:11





Use power of generic

– SPatel
Jan 3 at 10:11












4 Answers
4






active

oldest

votes


















3














If data can be a single object or an array write a custom initializer which decodes first an array, if a type mismatch error occurs decode a single object. data is declared as an array anyway.



As token appears only in a single object the property is declared as optional.



struct ApiData: Decodable {
let data : [DataObject]
let error : String?

private enum CodingKeys : String, CodingKey { case data, error }

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
data = try container.decode([DataObject].self, forKey: .data)
} catch DecodingError.typeMismatch {
data = [try container.decode(DataObject.self, forKey: .data)]
}
error = try container.decodeIfPresent(String.self, forKey: .error)
}
}


struct DataObject: Decodable {
let userId : Int
let token : String?

private enum CodingKeys: String, CodingKey { case userId = "id", token }
}


Edit: Your code to receive the data can be improved. You should add a better error handling to return also all possible errors:



func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

session.dataTask(with: urlRequest) {
(data, response, error) in
if let error = error { completion(nil, error); return }

if let responseCode = response as? HTTPURLResponse {
print("Response has status code: (responseCode.statusCode)")
}

do {
let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
completion(retreived, nil)
} catch {
print("Decoder error: ", error)
completion(nil, error)
}
}.resume()
}





share|improve this answer


























  • This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

    – CodeGeass
    Jan 3 at 10:12











  • You always nailed answer when it comes to Codable 🙌🏻

    – iVarun
    Jan 3 at 10:23



















1














Using power of generic, it simple like below:



struct ApiData<T: Decodable>: Decodable {
var data: T?
var error: String?
}

struct DataObject: Decodable {
private var id: Int?

var userId:Int? {
return id
}
}


Use



if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
//Do somthing
} else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
// Do somthing
}





share|improve this answer

































    0














    If you have only two possible outcomes for your data, an option would be to try and parse data to one of the expected types, if that fails you know that the data is of other type and you can then handle it accordingly.



    See this






    share|improve this answer


























    • Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

      – CodeGeass
      Jan 3 at 10:05



















    0














    You can try



    struct Root: Codable {
    let data: DataUnion
    let error: String?
    }

    enum DataUnion: Codable {
    case dataClass(DataClass)
    case datumArray([Datum])

    init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    if let x = try? container.decode([Datum].self) {
    self = .datumArray(x)
    return
    }
    if let x = try? container.decode(DataClass.self) {
    self = .dataClass(x)
    return
    }
    throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
    }

    func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    switch self {
    case .dataClass(let x):
    try container.encode(x)
    case .datumArray(let x):
    try container.encode(x)
    }
    }
    }

    struct Datum: Codable {
    let id: Int
    }

    struct DataClass: Codable {
    let id: Int
    let token: String
    }




    let res = try? JSONDecoder().decode(Root.self, from:data)





    share|improve this answer
























    • Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

      – CodeGeass
      Jan 3 at 10:10











    • you can make them 1 but should make token optional if you want

      – Sh_Khan
      Jan 3 at 10:11












    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54019877%2fdecoding-dynamic-json-structure-in-swift-4%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    3














    If data can be a single object or an array write a custom initializer which decodes first an array, if a type mismatch error occurs decode a single object. data is declared as an array anyway.



    As token appears only in a single object the property is declared as optional.



    struct ApiData: Decodable {
    let data : [DataObject]
    let error : String?

    private enum CodingKeys : String, CodingKey { case data, error }

    init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    do {
    data = try container.decode([DataObject].self, forKey: .data)
    } catch DecodingError.typeMismatch {
    data = [try container.decode(DataObject.self, forKey: .data)]
    }
    error = try container.decodeIfPresent(String.self, forKey: .error)
    }
    }


    struct DataObject: Decodable {
    let userId : Int
    let token : String?

    private enum CodingKeys: String, CodingKey { case userId = "id", token }
    }


    Edit: Your code to receive the data can be improved. You should add a better error handling to return also all possible errors:



    func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
    (data, response, error) in
    if let error = error { completion(nil, error); return }

    if let responseCode = response as? HTTPURLResponse {
    print("Response has status code: (responseCode.statusCode)")
    }

    do {
    let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
    completion(retreived, nil)
    } catch {
    print("Decoder error: ", error)
    completion(nil, error)
    }
    }.resume()
    }





    share|improve this answer


























    • This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

      – CodeGeass
      Jan 3 at 10:12











    • You always nailed answer when it comes to Codable 🙌🏻

      – iVarun
      Jan 3 at 10:23
















    3














    If data can be a single object or an array write a custom initializer which decodes first an array, if a type mismatch error occurs decode a single object. data is declared as an array anyway.



    As token appears only in a single object the property is declared as optional.



    struct ApiData: Decodable {
    let data : [DataObject]
    let error : String?

    private enum CodingKeys : String, CodingKey { case data, error }

    init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    do {
    data = try container.decode([DataObject].self, forKey: .data)
    } catch DecodingError.typeMismatch {
    data = [try container.decode(DataObject.self, forKey: .data)]
    }
    error = try container.decodeIfPresent(String.self, forKey: .error)
    }
    }


    struct DataObject: Decodable {
    let userId : Int
    let token : String?

    private enum CodingKeys: String, CodingKey { case userId = "id", token }
    }


    Edit: Your code to receive the data can be improved. You should add a better error handling to return also all possible errors:



    func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
    (data, response, error) in
    if let error = error { completion(nil, error); return }

    if let responseCode = response as? HTTPURLResponse {
    print("Response has status code: (responseCode.statusCode)")
    }

    do {
    let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
    completion(retreived, nil)
    } catch {
    print("Decoder error: ", error)
    completion(nil, error)
    }
    }.resume()
    }





    share|improve this answer


























    • This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

      – CodeGeass
      Jan 3 at 10:12











    • You always nailed answer when it comes to Codable 🙌🏻

      – iVarun
      Jan 3 at 10:23














    3












    3








    3







    If data can be a single object or an array write a custom initializer which decodes first an array, if a type mismatch error occurs decode a single object. data is declared as an array anyway.



    As token appears only in a single object the property is declared as optional.



    struct ApiData: Decodable {
    let data : [DataObject]
    let error : String?

    private enum CodingKeys : String, CodingKey { case data, error }

    init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    do {
    data = try container.decode([DataObject].self, forKey: .data)
    } catch DecodingError.typeMismatch {
    data = [try container.decode(DataObject.self, forKey: .data)]
    }
    error = try container.decodeIfPresent(String.self, forKey: .error)
    }
    }


    struct DataObject: Decodable {
    let userId : Int
    let token : String?

    private enum CodingKeys: String, CodingKey { case userId = "id", token }
    }


    Edit: Your code to receive the data can be improved. You should add a better error handling to return also all possible errors:



    func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
    (data, response, error) in
    if let error = error { completion(nil, error); return }

    if let responseCode = response as? HTTPURLResponse {
    print("Response has status code: (responseCode.statusCode)")
    }

    do {
    let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
    completion(retreived, nil)
    } catch {
    print("Decoder error: ", error)
    completion(nil, error)
    }
    }.resume()
    }





    share|improve this answer















    If data can be a single object or an array write a custom initializer which decodes first an array, if a type mismatch error occurs decode a single object. data is declared as an array anyway.



    As token appears only in a single object the property is declared as optional.



    struct ApiData: Decodable {
    let data : [DataObject]
    let error : String?

    private enum CodingKeys : String, CodingKey { case data, error }

    init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    do {
    data = try container.decode([DataObject].self, forKey: .data)
    } catch DecodingError.typeMismatch {
    data = [try container.decode(DataObject.self, forKey: .data)]
    }
    error = try container.decodeIfPresent(String.self, forKey: .error)
    }
    }


    struct DataObject: Decodable {
    let userId : Int
    let token : String?

    private enum CodingKeys: String, CodingKey { case userId = "id", token }
    }


    Edit: Your code to receive the data can be improved. You should add a better error handling to return also all possible errors:



    func makeDataTaskWith(with urlRequest: URLRequest, completion: @escaping(ApiData?, Error?) -> Void) {
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    session.dataTask(with: urlRequest) {
    (data, response, error) in
    if let error = error { completion(nil, error); return }

    if let responseCode = response as? HTTPURLResponse {
    print("Response has status code: (responseCode.statusCode)")
    }

    do {
    let retreived = try NetworkManager.shared.decoder.decode(ApiData.self, from: data!)
    completion(retreived, nil)
    } catch {
    print("Decoder error: ", error)
    completion(nil, error)
    }
    }.resume()
    }






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 3 at 10:23

























    answered Jan 3 at 10:10









    vadianvadian

    156k17166192




    156k17166192













    • This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

      – CodeGeass
      Jan 3 at 10:12











    • You always nailed answer when it comes to Codable 🙌🏻

      – iVarun
      Jan 3 at 10:23



















    • This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

      – CodeGeass
      Jan 3 at 10:12











    • You always nailed answer when it comes to Codable 🙌🏻

      – iVarun
      Jan 3 at 10:23

















    This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

    – CodeGeass
    Jan 3 at 10:12





    This is what i was looking for, didn't know of DecodingError.typeMismatch. Thank you!

    – CodeGeass
    Jan 3 at 10:12













    You always nailed answer when it comes to Codable 🙌🏻

    – iVarun
    Jan 3 at 10:23





    You always nailed answer when it comes to Codable 🙌🏻

    – iVarun
    Jan 3 at 10:23













    1














    Using power of generic, it simple like below:



    struct ApiData<T: Decodable>: Decodable {
    var data: T?
    var error: String?
    }

    struct DataObject: Decodable {
    private var id: Int?

    var userId:Int? {
    return id
    }
    }


    Use



    if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
    //Do somthing
    } else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
    // Do somthing
    }





    share|improve this answer






























      1














      Using power of generic, it simple like below:



      struct ApiData<T: Decodable>: Decodable {
      var data: T?
      var error: String?
      }

      struct DataObject: Decodable {
      private var id: Int?

      var userId:Int? {
      return id
      }
      }


      Use



      if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
      //Do somthing
      } else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
      // Do somthing
      }





      share|improve this answer




























        1












        1








        1







        Using power of generic, it simple like below:



        struct ApiData<T: Decodable>: Decodable {
        var data: T?
        var error: String?
        }

        struct DataObject: Decodable {
        private var id: Int?

        var userId:Int? {
        return id
        }
        }


        Use



        if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
        //Do somthing
        } else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
        // Do somthing
        }





        share|improve this answer















        Using power of generic, it simple like below:



        struct ApiData<T: Decodable>: Decodable {
        var data: T?
        var error: String?
        }

        struct DataObject: Decodable {
        private var id: Int?

        var userId:Int? {
        return id
        }
        }


        Use



        if let obj = try? NetworkManager.shared.decoder.decode(ApiData<DataObject>.self, from: data) {
        //Do somthing
        } else if let array = try NetworkManager.shared.decoder.decode(ApiData<[DataObject]>.self, from: data) {
        // Do somthing
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 3 at 11:47

























        answered Jan 3 at 10:27









        SPatelSPatel

        1,84121430




        1,84121430























            0














            If you have only two possible outcomes for your data, an option would be to try and parse data to one of the expected types, if that fails you know that the data is of other type and you can then handle it accordingly.



            See this






            share|improve this answer


























            • Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

              – CodeGeass
              Jan 3 at 10:05
















            0














            If you have only two possible outcomes for your data, an option would be to try and parse data to one of the expected types, if that fails you know that the data is of other type and you can then handle it accordingly.



            See this






            share|improve this answer


























            • Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

              – CodeGeass
              Jan 3 at 10:05














            0












            0








            0







            If you have only two possible outcomes for your data, an option would be to try and parse data to one of the expected types, if that fails you know that the data is of other type and you can then handle it accordingly.



            See this






            share|improve this answer















            If you have only two possible outcomes for your data, an option would be to try and parse data to one of the expected types, if that fails you know that the data is of other type and you can then handle it accordingly.



            See this







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Jan 3 at 10:05

























            answered Jan 3 at 10:04









            EdvinasEdvinas

            12




            12













            • Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

              – CodeGeass
              Jan 3 at 10:05



















            • Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

              – CodeGeass
              Jan 3 at 10:05

















            Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

            – CodeGeass
            Jan 3 at 10:05





            Yes but that would duplicate code, both types would have var userId: Int? and a bunch of other user info that i've not pasted in the example above.

            – CodeGeass
            Jan 3 at 10:05











            0














            You can try



            struct Root: Codable {
            let data: DataUnion
            let error: String?
            }

            enum DataUnion: Codable {
            case dataClass(DataClass)
            case datumArray([Datum])

            init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            if let x = try? container.decode([Datum].self) {
            self = .datumArray(x)
            return
            }
            if let x = try? container.decode(DataClass.self) {
            self = .dataClass(x)
            return
            }
            throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
            }

            func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            switch self {
            case .dataClass(let x):
            try container.encode(x)
            case .datumArray(let x):
            try container.encode(x)
            }
            }
            }

            struct Datum: Codable {
            let id: Int
            }

            struct DataClass: Codable {
            let id: Int
            let token: String
            }




            let res = try? JSONDecoder().decode(Root.self, from:data)





            share|improve this answer
























            • Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

              – CodeGeass
              Jan 3 at 10:10











            • you can make them 1 but should make token optional if you want

              – Sh_Khan
              Jan 3 at 10:11
















            0














            You can try



            struct Root: Codable {
            let data: DataUnion
            let error: String?
            }

            enum DataUnion: Codable {
            case dataClass(DataClass)
            case datumArray([Datum])

            init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            if let x = try? container.decode([Datum].self) {
            self = .datumArray(x)
            return
            }
            if let x = try? container.decode(DataClass.self) {
            self = .dataClass(x)
            return
            }
            throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
            }

            func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            switch self {
            case .dataClass(let x):
            try container.encode(x)
            case .datumArray(let x):
            try container.encode(x)
            }
            }
            }

            struct Datum: Codable {
            let id: Int
            }

            struct DataClass: Codable {
            let id: Int
            let token: String
            }




            let res = try? JSONDecoder().decode(Root.self, from:data)





            share|improve this answer
























            • Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

              – CodeGeass
              Jan 3 at 10:10











            • you can make them 1 but should make token optional if you want

              – Sh_Khan
              Jan 3 at 10:11














            0












            0








            0







            You can try



            struct Root: Codable {
            let data: DataUnion
            let error: String?
            }

            enum DataUnion: Codable {
            case dataClass(DataClass)
            case datumArray([Datum])

            init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            if let x = try? container.decode([Datum].self) {
            self = .datumArray(x)
            return
            }
            if let x = try? container.decode(DataClass.self) {
            self = .dataClass(x)
            return
            }
            throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
            }

            func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            switch self {
            case .dataClass(let x):
            try container.encode(x)
            case .datumArray(let x):
            try container.encode(x)
            }
            }
            }

            struct Datum: Codable {
            let id: Int
            }

            struct DataClass: Codable {
            let id: Int
            let token: String
            }




            let res = try? JSONDecoder().decode(Root.self, from:data)





            share|improve this answer













            You can try



            struct Root: Codable {
            let data: DataUnion
            let error: String?
            }

            enum DataUnion: Codable {
            case dataClass(DataClass)
            case datumArray([Datum])

            init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            if let x = try? container.decode([Datum].self) {
            self = .datumArray(x)
            return
            }
            if let x = try? container.decode(DataClass.self) {
            self = .dataClass(x)
            return
            }
            throw DecodingError.typeMismatch(DataUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for DataUnion"))
            }

            func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            switch self {
            case .dataClass(let x):
            try container.encode(x)
            case .datumArray(let x):
            try container.encode(x)
            }
            }
            }

            struct Datum: Codable {
            let id: Int
            }

            struct DataClass: Codable {
            let id: Int
            let token: String
            }




            let res = try? JSONDecoder().decode(Root.self, from:data)






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Jan 3 at 10:05









            Sh_KhanSh_Khan

            47.4k51433




            47.4k51433













            • Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

              – CodeGeass
              Jan 3 at 10:10











            • you can make them 1 but should make token optional if you want

              – Sh_Khan
              Jan 3 at 10:11



















            • Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

              – CodeGeass
              Jan 3 at 10:10











            • you can make them 1 but should make token optional if you want

              – Sh_Khan
              Jan 3 at 10:11

















            Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

            – CodeGeass
            Jan 3 at 10:10





            Thanks, but that doesn't avoid duplication. Both structs Datum and DataClass have let id.

            – CodeGeass
            Jan 3 at 10:10













            you can make them 1 but should make token optional if you want

            – Sh_Khan
            Jan 3 at 10:11





            you can make them 1 but should make token optional if you want

            – Sh_Khan
            Jan 3 at 10:11


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54019877%2fdecoding-dynamic-json-structure-in-swift-4%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            MongoDB - Not Authorized To Execute Command

            How to fix TextFormField cause rebuild widget in Flutter

            in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith