0

So basically what I want is for anytime that the API returns a type different than what the model expects to make that property nil.

For example:

struct Person {
var name: String?
var someType: String?
}

If the API returns a number for the someType property instead of a string I want to just make its value nil

I know I can do that implementing the init(from decoder: Decoder) initializer but then I'd have to set that up for every property of every response which would take a long time. Is there a better and quicker way?

JP Aquino
  • 3,946
  • 1
  • 23
  • 25

1 Answers1

0

You can implement your own decodeIfPresent for Optional String and return nil if there is a type mismatch. This way you wont have to implement a custom initializer:

extension KeyedDecodingContainer {
    public func decodeIfPresent(_ type: String.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> String? {
        do {
            return try decode(String.self, forKey: key)
        } catch DecodingError.typeMismatch, DecodingError.keyNotFound {
            return nil
        }
    }
}

struct Person: Decodable {
    let name: String?
    let someType: String?
}

let json = #"{"name":"Steve","someType":1}"#
do {
    let person = try JSONDecoder().decode(Person.self, from: Data(json.utf8))
    person.name      // "Steve"
    person.someType  // nil
} catch {
    print(error)
}

enter image description here

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • The first code is practically identically to just writing `try?` and your second code isn't using the `KeyedDecodingContainer` at all... – Claus Jørgensen Oct 14 '20 at 21:57
  • @ClausJørgensen Are you sure? it works for me – Leo Dabus Oct 14 '20 at 22:00
  • 1
    It's because you're using a extension to override an existing method. It's seriously bad practice. Also again, you could just write it as a single try? statement. See the linked duplicate. – Claus Jørgensen Oct 14 '20 at 22:04
  • @ClausJørgensen I am not overwriting anything. KeyedDecodingContainer is a struct not a class – Leo Dabus Oct 14 '20 at 22:06
  • @ClausJørgensen I am providing a solution for what is being asked. I wont delete my post just because of your opinion – Leo Dabus Oct 14 '20 at 22:08
  • @ClausJørgensen regarding your comment about override existing method it doesn'tt work. https://stackoverflow.com/a/63422823/2303865 – Leo Dabus Oct 14 '20 at 22:10
  • Thanks Leo. It seems like a good solution and I see that it works on your code. Can't test on my project now because the server is down. – JP Aquino Oct 14 '20 at 22:15
  • @JPAquino you are welcome – Leo Dabus Oct 14 '20 at 22:15
  • I have a question. When does your decodeIfPresent method gets called? – JP Aquino Oct 14 '20 at 22:16
  • 1
    @LeoDabus so you're defining a extension, not calling it explicitly, but think it's not a problem that it's getting called?-) I guess this is what happens when you copy&paste answers from other questions :p – Claus Jørgensen Oct 14 '20 at 22:16
  • @ClausJørgensen NewDev learned this appraoch from one of my answers https://stackoverflow.com/a/63803573/2303865 – Leo Dabus Oct 14 '20 at 22:21
  • I suppose maybe we only get nil on the playground but on a real project that code would still throw a Type Mismatch error and not parse the response? – JP Aquino Oct 14 '20 at 22:22
  • @JPAquino No. It does work. Take your time to test it. It doesn't matter if it is on playground or not. – Leo Dabus Oct 14 '20 at 22:29
  • 1
    @LeoDabus... actually from https://github.com/marksands/BetterCodable... which uses that approach too, but against their own custom type. So, I'm personally not a fan of an approach that holistically overrides a default behavior for a widely used type, and prefer the property wrapper approach – New Dev Oct 14 '20 at 22:31
  • 1
    @NewDev I know already you prefer the property wrapper. I am answering what is being asked here. A way to avoid implementing a custom initializer. – Leo Dabus Oct 14 '20 at 22:34
  • @NewDev Btw if you use `try?` you will discard all Decoding errors. I've already showed you how to catch a specific error. – Leo Dabus Oct 14 '20 at 22:39
  • 1
    @ClausJørgensen, Leo definitely didn't *just* copy-paste, so I think it's an unfounded accusation that doesn't belong on SO. – New Dev Oct 14 '20 at 22:55
  • @LeoDabus, it's not that I didn't know how to catch errors, but decided to simplify an answer to illustrate a concept. Perhaps it was an over-simplification - I should probably fix the other answer. – New Dev Oct 14 '20 at 22:55