I can't decode yyyy-MM-dd format date well with json returned in response

478 Views Asked by At

Always I am indebted. I'm currently using Moya and trying to decode the response returned from the API. However, when decoding, the registered_at key cannot be decoded properly. The returned registered_at value is in yyyy-MM-dd format, for example "2021-07-26". The error that is occurring is

▿ Moya Error
  ▿ objectMapping: 2 elements
    ▿ .0: DecodingError
      ▿ typeMismatch: 2 elements
        --.0: Swift.Double
        ▿ .1: Context
          ▿ codingPath: 1 element
            --0: CodingKeys (stringValue: "registered_at", intValue: nil)
          --debugDescription: "Expected to decode Double but found a string / data instead."
          --undergroundError: nil
    ▿ .1: Status Code: 200, Data Length: 137

is

The json passed as a response is below.

{
    "message": "patience has been registered",
    "id": 11,
    "money": 200,
    "memo": "test",
    "category_title": "Test",
    "registered_at": "2021-08-17"
}

What I tried

I tried to specify that Moya should handle'yyyy-MM-dd' in the dateDecodingStrategy, which determines how to decode the Date type, but the error continued to appear.

Code

ApiClient.swift

class ApiClient: ApiClientInterface {
    private init () {}

    static let shared = ApiClient ()

    func request <T> (_ request: T, callbackQueue: DispatchQueue = .main, completion: @escaping (Result <T.Response, MoyaResponseError>)-> Void) where T: ApiTargetType {
        let provider = MoyaProvider <T> ()
        provider.request (request, callbackQueue: callbackQueue) {result in
            let decoder = JSONDecoder ()
            decoder.dateDecodingStrategy = .formatted (.yyyyMMdd)
            switch result {
            case let .success (response):
                if let model = try? response.map (T.Response.self, using: decoder) {
                    completion (.success (model))
                } else if let errorModel = try? response.map (ErrorResponse.self) {
                    completion (.failure (.badRequestError (errorModel.code)))
                } else {
                        completion (.failure (.unknownError))
                    }

            case let .failure (moyaError):
                completion (.failure (.moyaError (moyaError)))
            }
        }
    }
}

PatienceEntity.swift

struct PatienceEntity: Decodable {
    let id: Int
    let registeredAt: Date
    let memo: String
    let money: Int
    let categoryTitle: String

    enum CodingKeys: String, CodingKey {
        case id
        case registeredAt = "registered_at"
        case memo
        case money
        case categoryTitle = "category_title"
    }
}

CreatePatienceTargetType.swift

struct CreatePatienceTargetType: ApiTargetType {
    typealias Response = PatienceEntity

    let registeredAt: Date

    let money: Int

    let memo: String

    let categoryTitle: String

    var parameters: [String: Any] {["money": money, "memo": memo, "category_title": categoryTitle, "registered_at": registeredAt]}

    var path: String {"/ patiences"}

    var method: Moya.Method {.post}

    var sampleData: Data {Data ()}

    var task: Task {.requestParameters (parameters: parameters, encoding: URLEncoding.queryString)}
}

ApiTargetType.swift

protocol ApiTargetType: TargetType {
    associatedtype Response: Decodable
}

extension ApiTargetType {
    var baseURL: URL {URL (string: "requesting baseURL")!}
    var headers: [String: String]? {
        let token = TokenManager.getToken ()
       return ["Content-Type": "application / json"]
    }
}

DateFormatter.swift

extension DateFormatter {
    static let yyyyMMdd: DateFormatter = {
        let formatter = DateFormatter ()
        formatter.dateFormat = "yyyy-MM-dd"
        formatter.calendar = Calendar (identifier: .gregorian)
        formatter.locale = Locale (identifier: "en_US_POSIX")
        return formatter
    } ()
}
1

There are 1 best solutions below

0
Trevor On
  1. Recheck your JSON and validate it
  2. I recommend decoding with .convertFromSnakecase to save time especially when your json contains much more data
  3. Decode it as a string and convert to date later. This would simplify your decoding process and according to the json you posted, the registeredAt is a String not Date as far as I can tell. Either way decoding then converting is typically safer. And often results in less code with more options for added features/data morphing in the future.