1
votes

Let's say I need to transform a date string I received from a web service to a Date object.

Using ObjectMapper, that was easy:

class Example: Mappable {
    var date: Date?

    required init?(map: Map) { }

    func mapping(map: Map) {  
        date <- (map["date_of_interest"], GenericTransform().dateTransform)
    }
}

I just had to implement a tranformer ("GenericTransform" in this case) for date, and pass it as an argument along with the key name to decode.

Now, using Codable:

class Example2: Codable {
    var name: String
    var age: Int
    var date: Date?

    enum CodingKeys: String, CodingKey {
        case name, age
        case date = "date_of_interest"
    }
}

To transform a date, in my understanding, I'd have to either:

1) Pass a dateDecodingStrategy to my JSONDecoder, which I don't want to, because I'm trying to keep that part of the code as a generic function.

or

2) Implement an init(from decoder: Decoder) inside Example2, which I also don't want to, because of the boilerplate code I'd have to write to decode all the other properties (which would be automatically generated otherwise):

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    age = try container.decode(Int.self, forKey: .age)
    let dateString = try container.decode(String.self, forKey: .date)
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    if let date = formatter.date(from: dateString) {
        self.date = date
    } else {
        //throw error
    }
}

My question is: is there an easier way to do it than options 1 and 2 above? Maybe tweaking the CodingKeys enum somehow?

EDIT:

The problem is not only about dates, actually. In this project that I'm working on, there are many custom transformations being done using TransformOf<ObjectType, JSONType> from ObjectMapper.

For example, a color transformation of a hex code received from a web service into a UIColor is done using this bit of code:

let colorTransform = TransformOf<UIColor, String>(fromJSON: { (value) -> UIColor? in
    if let value = value {
        return UIColor().hexStringToUIColor(hex: value)
    }
    return nil
}, toJSON: { _ in
    return nil
})

I'm trying to remove ObjectMapper from the project, making these same transformations using Codable, so only using a custom dateDecodingStrategy will not suffice.

How would you guys do it? Implement a custom init(from decoder: Decoder) for every class that has to decode, for example, a color hex code?

1
"1) Pass a dateDecodingStrategy to my JSONDecoder, which I don't want to" Why not? - Leo Dabus
If you need a flexible date decoding strategy this might be what you are looking for stackoverflow.com/a/46458771/2303865 - Leo Dabus
Forget about object mapping and focus your efforts in learning how to properly encode/decode your JSON each case individually. You should take some time and read Reflecting on Reflection developer.apple.com/swift/blog/?id=37 - Leo Dabus
This might help but again the question as it is It is too broad stackoverflow.com/questions/48493317/… - Leo Dabus

1 Answers

3
votes

Using dateDecodingStrategy in your case (as you only reference a single date format) is very simple…

let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)