title: Swift Decodable Dates with Empty Strings or Nil
body: <h2>Situation</h2>
  <p>I’m dealing with an API server that has multiple Date fields and I have seen that the API response for these Date fields can be:</p>
  <pre><code>{
   &quot;clicktimestamp&quot;: &quot;&quot;,
   &quot;clicktimestamp&quot;: &quot;  &quot;,  
   &quot;clicktimestamp&quot;: &quot;2020-08-08 16:13:17&quot;
  }
  </code></pre>
  <p><strong>The JSON response could be:</strong><br />
  • String (no white space)<br />
  • String (with whitespace)<br />
  • String with some Date format.</p>
  <p>I don’t have access to the API server and I can’t ask the server side engineer to change it. My situation is not ideal so I have to deal with it.</p>
  <h2>Working Solution  (Not Very Swifty)</h2>
  <p>I wrote some code to deal with this situation. It works but <strong>something about it does not feel very Swift</strong>.</p>
  <p>How can I improve this whole decoding process considering my JSON response situation?<br />
  Or is it good?</p>
  <p>Here is a working solution:</p>
  <pre><code>import UIKit
  import Foundation
  
  struct ProductDate: Decodable, Hashable {
      var lastcheckedtime: Date?
      var oktime: Date?
      var clicktimestamp: Date?
      var lastlocaltime: Date?
      // I have more properties but I'm omitting them
  }
  
  extension ProductDate {
      
      private enum Keys: String, CodingKey {
          case lastcheckedtime
          case oktime
          case clicktimestamp
          case lastlocaltime
      }
      
      init(from decoder: Decoder) throws {
          
          let formatter = DateFormatter.yyyyMMdd
          let container = try decoder.container(keyedBy: Keys.self)
      
          let dateKeys: [KeyedDecodingContainer&lt;Keys&gt;.Key] = [
              .lastcheckedtime,
              .oktime,
              .clicktimestamp,
              .lastlocaltime
          ]
  
          let parseDate: (String, KeyedDecodingContainer&lt;Keys&gt;.Key, KeyedDecodingContainer&lt;Keys&gt;) throws -&gt; Date? = {(dateString, someKey, container) in
               if !dateString.isEmpty {
                   if let date = formatter.date(from: dateString) {
                       return date
                   } else {
                       throw DecodingError.dataCorruptedError(forKey: someKey,
                                       in: container,
                                       debugDescription: &quot;Date string does not match format expected by formatter.&quot;)
                   }
               } else {
                   return nil
               }
           }
  
          let datesResults: [Date?] = try dateKeys.map({ key in
              // 1.  decode as a string because we sometimes get &quot;&quot; or  &quot; &quot; for those date fields as the API server is poorly managed.
              let dateString = try container.decode(String.self, forKey: key)
                                              .trimmingCharacters(in: .whitespaces)
              // 2. now pass in our dateString which could be &quot;&quot; or &quot; &quot; or &quot;2020-08-08 16:13:17&quot;
              // and try to parse it into a Date or nil
              let result = try parseDate(dateString, key, container)
              return result
          })
  
          // 3. Assign our array of dateResults to our struct keys
          lastcheckedtime = datesResults[0]
          oktime          = datesResults[1]
          clicktimestamp  = datesResults[2]
          lastlocaltime   = datesResults[3]
          
          
      }
      
  }
  
  
  extension DateFormatter {
    static let yyyyMMdd: DateFormatter = {
      let formatter = DateFormatter()
      formatter.dateFormat = &quot;YYYY-MM-DD HH:mm:ss&quot;
      formatter.calendar = Calendar(identifier: .iso8601)
      formatter.timeZone = TimeZone(secondsFromGMT: 0)
      formatter.locale = Locale(identifier: &quot;en_US_POSIX&quot;)
      return formatter
    }()
  }
  
  
  let json = &quot;&quot;&quot;
  {
     &quot;lastcheckedtime&quot;: &quot;&quot;,
     &quot;oktime&quot;: &quot; &quot;,
     &quot;clicktimestamp&quot;: &quot;&quot;,
     &quot;lastlocaltime&quot;: &quot;2020-08-08 16:13:17&quot;
  }
  &quot;&quot;&quot;.data(using: .utf8)!
  
  
  let decoder = JSONDecoder()
  
  
  print(json)
  
  do {
      let decoded = try decoder.decode(ProductDate.self, from: json)
      print(decoded)
  } catch let context {
     print(context)
  }
  
  
  </code></pre>