Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert string to Date/Int/Double using codable

I am getting a response from an API but the problem is that the API is sending values back as a string of dates and doubles. I am therefore getting the error "Expected to decode Double but found a string/data instead." I have structured my struct like this to solve the problem but this seems like a patch. Is there any better way to fix this issue? I feel like apple has thought of this and included something natively to address this.

struct SimpleOrder:Codable{ 
    var createdDate:Date! {
        return createdDateString.dateFromISO8601
    }
    var createdDateString:String

    var orderId:String!

    var priceVal:Double!
    var priceString:String{
        didSet {
            priceVal = Double(self.priceString)!
        }
    }

    private enum CodingKeys: String, CodingKey {
        //case createdDate
        case createdDateString = "time"

        case orderId = "id"

        case priceVal
        case priceString = "price"
    }
}

I don't know if this is relevant but this is how it is being used. I am getting the data as a string and converting it to data which is stored in the dataFromString variable

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601 //This is irrelevant though because the date is returned as a string. 

do{
    let beer = try decoder.decode(SimpleOrder.self, from: dataFromString)
    print ("beer is \(beer)")
}catch let error{
    print ("error is \(error)")
}

As a result of using codable, I am getting an error when trying to get an empty instance of SimpleOrder. Before I was using codable, I had no issues using SimpleOrder() without any arguments.

Error: Cannot invoke initializer for type 'SimpleOrder' with no arguments

var singlePoint = SimpleOrder()
like image 808
Nevin Jethmalani Avatar asked Dec 01 '17 19:12

Nevin Jethmalani


1 Answers

struct SimpleOrder: Codable {
    var created: Date?
    var orderId: String?
    var price: String?
    private enum CodingKeys: String, CodingKey {
        case created = "time", orderId = "id", price
    }
    init(created: Date? = nil, orderId: String? = nil, price: String? = nil) {
        self.created = created
        self.orderId = orderId
        self.price = price
    }
}

extension SimpleOrder {
    var priceValue: Double? {
        guard let price = price else { return nil }
        return Double(price)
    }
}

extension Formatter {
    static let iso8601: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
        return formatter
    }()
}

Decoding the json data returned by the API:

let jsonData = Data("""
{
    "time": "2017-12-01T20:41:48.700Z",
    "id": "0001",
    "price": "9.99"
}
""".utf8)

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(Formatter.iso8601)

do {
    let simpleOrder = try decoder.decode(SimpleOrder.self, from: jsonData)
    print(simpleOrder)
} catch {
    print(error)
}

Or initialising a new object with no values:

var order = SimpleOrder()
like image 61
Leo Dabus Avatar answered Nov 04 '22 14:11

Leo Dabus