Search code examples
jsonswiftapinsarray

How to parse this type of data to a JSON in Swift?


I have called an API to get all holidays in a year, it came out a Json type. But I only can extract it like below (it is one of many elements of "items")

"items": [
  {
   "kind": "calendar#event",
   "etag": "\"3235567993214000\"",
   "id": "20200101_1814eggq09ims8ge9pine82pclgn49rj41262u9a00oe83po05002i01",
   "status": "confirmed",
   "htmlLink": "https://www.google.com/calendar/event?eid=MjAyMDAxMDFfMTgxNGVnZ3EwOWltczhnZTlwaW5lODJwY2xnbjQ5cmo0MTI2MnU5YTAwb2U4M3BvMDUwMDJpMDEgZW4udWsjaG9saWRheUB2",
   "created": "2021-04-07T08:26:36.000Z",
   "updated": "2021-04-07T08:26:36.607Z",
   "summary": "New Year's Day",
   "creator": {
    "email": "en.uk#holiday@group.v.calendar.google.com",
    "displayName": "Holidays in United Kingdom",
    "self": true
   },
   "organizer": {
    "email": "en.uk#holiday@group.v.calendar.google.com",
    "displayName": "Holidays in United Kingdom",
    "self": true
   },
   "start": {
    "date": "2020-01-01"
   },
   "end": {
    "date": "2020-01-02"
   },
   "transparency": "transparent",
   "visibility": "public",
   "iCalUID": "20200101_1814eggq09ims8ge9pine82pclgn49rj41262u9a00oe83po05002i01@google.com",
   "sequence": 0,
   "eventType": "default"
  },
  {
   "kind": "calendar#event",
   "etag": "\"3235567993214000\"",
   "id": "20200412_1814eggq09ims8gd8lgn6t35e8g56tbechgniag063i0ue048064g0g",
   "status": "confirmed",
   "htmlLink": "https://www.google.com/calendar/event?eid=MjAyMDA0MTJfMTgxNGVnZ3EwOWltczhnZDhsZ242dDM1ZThnNTZ0YmVjaGduaWFnMDYzaTB1ZTA0ODA2NGcwZyBlbi51ayNob2xpZGF5QHY",
   "created": "2021-04-07T08:26:36.000Z",
   "updated": "2021-04-07T08:26:36.607Z",
   "summary": "Easter Sunday",
   "creator": {
    "email": "en.uk#holiday@group.v.calendar.google.com",
    "displayName": "Holidays in United Kingdom",
    "self": true
   },
   "organizer": {
    "email": "en.uk#holiday@group.v.calendar.google.com",
    "displayName": "Holidays in United Kingdom",
    "self": true
   },
   "start": {
    "date": "2020-04-12"
   },
   "end": {
    "date": "2020-04-13"
   },
   "transparency": "transparent",
   "visibility": "public",
   "iCalUID": "20200412_1814eggq09ims8gd8lgn6t35e8g56tbechgniag063i0ue048064g0g@google.com",
   "sequence": 0,
   "eventType": "default"
  }

I try to get the value in key "start" and key "summary" but I can't.

Xcode told me that "items" is a __NSArrayI type.

What I've tried so far is create a class simple like this (just use to try first, so I didn't make all variable)

class API_Info {
var kind: String?
var etag: String?
var id: String?
var status: String?
var htmlLink: String?
var created: String?
var updated: String?
var summary: String?

init(items: [String:Any]){
    self.kind = items["kind"] as? String
    self.etag = items["etag"] as? String
    self.id = items["id"] as? String
    self.status = items["status"] as? String
    self.htmlLink = items["htmlLink"] as? String
    self.created = items["created"] as? String
    self.updated = items["updated"] as? String
    self.summary = items["summary"] as? String
 }
}

And I parse like this:

guard let items = json!["items"]! as? [API_Info] else{
                print("null")
            return
        }

In this way, else statement was run.

What am I doing wrong, and how can I get the data I want?

Thanks in advance.


Solution

  • Codable is the solution here. Below is the struct I used rather than your class

    struct ApiInfo: Codable {
        let kind: String
        let etag: String
        let id: String
        let status: String
        let htmlLink: String
        let created: Date 
        let updated: Date
        let summary: String
    }
    

    Then I created a root type to hold the array

    struct Result: Codable {
        let items: [ApiInfo]
    }
    

    And then the decoding

    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
    
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(formatter)
    
    do {
        let result = try decoder.decode(Result.self, from: data)
        print(result.items)
    } catch {
        print(error)
    }
    

    Notice that the data values are decoded to Date objects. Optionally you can skip the root type and decode as a dictionary

    do {
        let items = try decoder.decode([String: [ApiInfo]].self, from: data)
        if let values = items["items"] {
            print(values)
        }
    } catch {
        print(error)
    }