Get observed public holidays list from Google API

There is a useful Google API available to developers who register with the Google Console and obtain an API key. For a chosen country, this API returns a list of dates which are public holidays. 

However, there are problems with the default output, e.g

- a single public holiday may in some cases have an original date and an observed (if, for example, the original date is not possible e.g. is on a weekend). The API returns both dates as separate items.
- multiple holidays can are given with different dates but identical names
- a single public holiday date can be duplicated, with some instances including an extra "holiday" suffix in their name
- holidays with identical names but from multiple years (both previous, current and future years) are given


The method for extracting a list of OBSERVED public holidays in Japan, in Japanese language is presented here for iOS/Swift

### URL

```swift
https://www.googleapis.com/calendar/v3/calendars/{LANGUAGE}.{COUNTRY}%23holiday%40group.v.calendar.google.com/events?key={API_KEY}
```

where

- {LANGUAGE} is the requested language, e.g: ja, en
- {COUNTRY} is the requested country, e.g: japanese, uk, french
- {API_KEY} is the API key from your developer account

### Define a custom date handler object

This object helps to compare dates extracted from the API

```swift
struct PublicHoliday{
    
    // some holiday names are provided with an "observed" suffix - so we define a base name (without this suffix) to more easily compare dates 
    let baseName: String
    let name: String
    let start: String
    let end: String
    
    let observedKeyword: String = "observed"
    
    // let dates with the "observed" keyword take priority (override) for a specific holiday date
    var isOverride: Bool {
        get {
            return self.name.contains(observedKeyword)
        }
    }
    
    
    init(name: String, start: String, end: String) {
        self.name = name
        self.start = start
        self.end = end
        
        let components = name.split(separator: " ")
        if let last = components.last, last == observedKeyword {
            self.baseName = components.dropLast().joined(separator: " ")
        }
        else {
            self.baseName = name
        }
    }
        
    init(date: String) {
        self.init(name: "", start: date, end: date)
    }
}
```


### Parse Response in Swift

The following parse function extracts only observed public holidays for the current as an array of Public Holiday objects

```swift
class GoogleCalendarAPIParser {
    
    
    static func process(json: [String: Any]) -> [PublicHoliday] {
        
        guard let items = json["items"] as? JSONArray,
            0 < items.count else {
            return []
        }
        
        // extract all dates
        var dates: [PublicHoliday] = items.compactMap { item in
            
            guard
                let name= item["summary"] as? String,
                let start = item["start"] as? [String: Any],
                let end = item["start"] as? [String: Any],
                let startDate = start["date"] as? String,
                let endDate = end["date"] as? String
            else {
                Swift.print("unable to parse dates", item)
                return nil
            }
            
            return PublicHoliday(
                name: name,
                start: startDate,
                end: endDate
            )
        }
        
        
        // keep only dates from the current year
        let currentYear = String(Calendar.current.component(.year, from: Date()))
        dates = dates.filter { $0.startDate.split("-")[0] == currentYear }
        
        // keep dates that are overrides (the "observed" date) and dates which are not overriden by other dates
        dates = dates.filter { date in
            let isOverride = date.isOverride
            let hasNoOverride = dates.filter {
                $0.baseName == date.baseName && $0.isOverride
                }.isEmpty
            return isOverride || hasNoOverride
        }
        return dates
    } 
}
```