问题
I am using Codable
protocol from Swift 4 first time, I am not able to understand use of decodeIfPresent
from Decodable
.
/// Decodes a value of the given type for the given key, if present.
///
/// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call.
///
/// - parameter type: The type of value to decode.
/// - parameter key: The key that the decoded value is associated with.
/// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value.
/// - throws: `DecodingError.typeMismatch` if the encountered encoded value is not convertible to the requested type.
public func decodeIfPresent(_ type: String.Type, forKey key: KeyedDecodingContainer.Key) throws -> String?
Here it suggest that it returns nil
, if value not present with associated key. If this is the only reason , then how it differ from optional property, as optional variable also set to nil
if value is not present in response.
回答1:
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.
回答2:
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
.
来源:https://stackoverflow.com/questions/46292325/what-is-difference-between-optional-and-decodeifpresent-when-using-decodable-for