0
votes

I've JSON data, which is an array of dictionary, wherein the first item is different from the rest of the array items.

Sample JSON

[
  {
    "totalDays": 180,
    "totalWorkingDays": 148,
    "totalWorkingHours": "1184:00:00",
    "totalWorkedHours": "985:23:10",
    "totalShortageHours": "-199:37:50",
    "avgWorkingHours": "06:39:28(83%)",
    "avgShortageHours": "01:20:31(17%)",
    "totalAbsents": 13,
    "totalLeaves": 4
  },
  {
    "attendanceDayCount": 1,
    "attendanceDate": "21-Jan-19",
    "attendanceDay": "Monday"
  },
  {
    "attendanceDayCount": 2,
    "attendanceDate": "22-Jan-19",
    "attendanceDay": "Tuesday"
  },
  {
    "attendanceDayCount": 3,
    "attendanceDate": "23-Jan-19",
    "attendanceDay": "Wednesday"
  }
]

I'm using Codable and my struct looks like this

struct DashboardSummary : Codable {
    
   var totalDays : Int?
   var totalWorkingDays : Int?
   var totalWorkingHours : String?
   var totalWorkedHours : String?
   var totalShortageHours : String?
   var avgWorkingHours : String?
   var avgShortageHours : String?
   var totalAbsents : Int?
   var totalLeaves : Int?
   
} 

struct DaySummary : Codable {

   var attendanceDayCount : Int?
   var attendanceDate : String?
   var attendanceDay : String?    

}

EDIT: JSON Decoding from Network request method

DispatchQueue.main.async {
   if let responseObject = try? JSONDecoder().decode(Response.self, from: data) {
     print("responseObject", responseObject)
     completion(.success(responseObject))
   }
   else {
     let error = NSError(domain: "", code: 200, userInfo: [NSLocalizedDescriptionKey : "Failed to decode"])
     completion(.failure(error))
   }
 }

Things I've tried

  1. I passed 'DashboardSummary' struct as codable through my network request, which did give results for the first item and of-course nil value for the rest of the array.

  2. I tried init(from decoder: Decoder) and func encode(to encoder: Encoder) which unfortunately didn't work for me.

How can I get around this?

1
Never, never ever ignore the Decoding error and create a custom completely meaningless NSError instance. Add a do - catch block and print the caught error instance. It tells you exactly what's wrong.vadian
Point taken. There was no problem with @Sweeper's answer. The problem was with another struct. And scrapping custom NSError with do-catch point right to the actual error. Thanksuzair

1 Answers

1
votes

First, you need to create a third struct that can represent the array, for example:

struct Response : Codable {
    var dashboardSummary: DashboardSummary?
    var daySummaries: [DaySummary]
    
    enum CodingKeys: CodingKey {
        case dashboardSummary, daySummaries
    }
}

Then you need to implement init(from:) and encode(to:) to do custom coding.

You can use an unkeyedContainer to decode/encode it. When decoding, you keep decoding from the container while the container is not isAtEnd.

init(from decoder: Decoder) throws {
    var container = try decoder.unkeyedContainer()
    dashboardSummary = try container.decodeIfPresent(DashboardSummary.self)
    daySummaries = []
    while !container.isAtEnd {
        daySummaries.append(try container.decode(DaySummary.self))
    }
}

func encode(to encoder: Encoder) throws {
    var container = encoder.unkeyedContainer()
    try container.encode(dashboardSummary)
    for daySummary in daySummaries {
        try container.encode(daySummary)
    }
}

You can also consider making the properties let constants if you don't intend on changing them.