I am using Codable
protocol from Swift 4 first time, I am not able to understand use of decodeIfPresent
from Decodable
.
I think it makes sense to use decodeifPresent
rather than an optional property if you want to use a default value for a property that could be missing from the JSON.
For example, let's examine 3 situations:
1. All the keys are present in the JSON:
Let's suppose you must decode this JSON:
{
"project_names": ["project1", "project2", "project3"],
"is_pro": true
}
You can use this struct:
struct Program: Codable {
let projectNames: [String]
let isPro: Bool
}
and you will get a Program
object with a isPro
value equal to true
.
(I suppose your decoder keyDecodingStrategy
is .convertFromSnakeCase
in the rest of this example)
2. Some keys are missing in the JSON and you're ok to have an optional in Swift:
{
"project_names": ["project1", "project2", "project3"]
}
You can now use this struct:
struct Program: Codable {
let projectNames: [String]
var isPro: Bool?
}
and you will get a Program
object with a isPro
value equal to nil
.
If the JSON looked like this:
{
"project_names": ["project1", "project2", "project3"],
"is_pro": true
}
then isPro
would be a Bool?
with value true
.
Maybe that's what you want, but probably you would like to have a Bool
with a default value of false
. That's where decodeIfPresent
could be useful.
3. Some keys are missing in the JSON and you want a non-optional property with a default value in Swift:
If your struct looks like this:
struct Program: Codable {
let projectNames: [String]
var isPro: Bool = false
}
then you will get a parsing error if the "is_pro" attribute is not present in your JSON. Because Codable expects to find a value to parse a Bool property.
In that situation, a good idea would be to have an initializer with decodeIfPresent
, like so:
struct Program: Codable {
let projectNames: [String]
let isPro: Bool
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.projectNames = try container.decode([String].self, forKey: .projectNames)
self.isPro = try container.decodeIfPresent(Bool.self, forKey: .isPro) ?? false
}
}
This allows you to have the best of both worlds:
Bool
, not a Bool?
propertyfalse
if the field is not present in the JSON.