What is difference between optional and decodeIfPresent when using Decodable for JSON Parsing?

There's a subtle, but important difference between these two lines of code:

// Exhibit 1
foo = try container.decode(Int?.self, forKey: .foo)
// Exhibit 2
foo = try container.decodeIfPresent(Int.self, forKey: .foo)

Exhibit 1 will parse:

{
  "foo": null,
  "bar": "something"
}

but not:

{
  "bar": "something"
}

while exhibit 2 will happily parse both. So in normal use cases for JSON parsers you'll want decodeIfPresent for every optional in your model.


Yes, @Sweeper's comment makes a sense.

I will try to explain it according to my understanding.

public class User : Decodable{

    public var firstName:String
    public var lastName:String
    public var middleName:String?
    public var address:String
    public var contactNumber:String


    public enum UserResponseKeys: String, CodingKey{
        case firstName = "first_name"
        case lastName = "last_name"
        case middleName = "middle_name"
        case address = "address"
        case contactNumber = "contact_number"
    }

    public required init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: UserResponseKeys.self)

        self.firstName = try container.decode(String.self, forKey: .firstName)
        self.lastName = try container.decode(String.self, forKey: .lastName)
        self.middleName = try container.decodeIfPresent(String.self, forKey: .middleName)
        self.address = try container.decode(String.self, forKey: .address)
        self.contactNumber = try container.decode(String.self, forKey: .contactNumber)
    }

}

Above is my User class, in which I marked middleName as optional parameter, because it may possible that JSON response may not provide middleName key-value pair in response, so we can use decodeIfPresent.

self.middleName = try container.decodeIfPresent(String.self, forKey: .middleName)

While for others variables which are mandatory fields so we are sure that no need to use of optional for that. We used only decode for that as that method does not return optional.

public func decode(_ type: String.Type, forKey key: KeyedDecodingContainer.Key) throws -> String

Above decode function returns String while decodeIfPresent returns String?, so we can use optional variable to store that.

So final conclusion is that if you are not sure of service response contract or you may dealing with any third party services where JSON response and parameters may change without your knowledge then you can use decodeIfPresent so it can handle absence of particular parameter in response and set value as nil.