Swift Codable Decode Manually Optional Variable

Swift Codable Decode Manually Optional Variable

Age is optional:

let age: String? 

So try to decode in this way:

let age: String? = try values.decodeIfPresent(String.self, forKey: .age)

Best approach to create Non-optional Codable with `default values` in swift

You can implement a custom decoder with default values:

class CompanyInfo : Codable {
var NameEn: String
var CityEn: String
var Website: String
var Email: String
var Phone: String
var Fax: String

required init(from decoder: Decoder) throws {
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.NameEn = try container.decodeIfPresent(String.self, forKey: .NameEn) ?? "Default"
self.CityEn = try container.decodeIfPresent(String.self, forKey: .CityEn) ?? "Default"
self.Website = try container.decodeIfPresent(String.self, forKey: .Website) ?? "Default"
self.Email = try container.decodeIfPresent(String.self, forKey: .Email) ?? "Default"
self.Phone = try container.decodeIfPresent(String.self, forKey: .Phone) ?? "Default"
self.Fax = try container.decodeIfPresent(String.self, forKey: .Fax) ?? "Default"
}
}
}

Unrelated to question, but important Note:

In Swift, only Types names should start with a capital letter. If you continue naming variables like this, you will have a serious refactoring issue one day if you decide to use CoreData or working with other Swift developers.

Codable: give a default value to a new non-optional property

You can implement required init and give it a default value:

required init(from decoder: Decoder) throws {

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

let title = try container.decodeIfPresent(String.self, forKey: .title) ?? "Default title"
self.title = title

let baseDecoder = try container.superDecoder(forKey: .id)

try super.init(from: baseDecoder)
}

Error decoding data with Codable even though parameters are optional

When you implement init(from:) manually you need to use the decodeIfPresent(_:forKey:) variant for optional properties. The decode(_:forKey:) method throws an error if a nullable field in the JSON data is absent whereas the decodeIfPresent(_:forKey:) method just returns nil.

Try this:

init(from decoder: Decoder) throws {

enum DecodingKeys: CodingKey {
case minutes
}

let container = try decoder.container(keyedBy: DecodingKeys.self)
minutes = try container.decodeIfPresent(Int.self, forKey: .minutes)
}

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.

How to manually decode an an array in swift 4 Codable?

Your code doesn't compile due to a few mistakes / typos.

To decode an array of Int write

struct Something: Decodable {
var value: [Int]

enum CodingKeys: String, CodingKey {
case value
}

init (from decoder :Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
value = try container.decode([Int].self, forKey: .value)
}
}

But if the sample code in the question represents the entire struct it can be reduced to

struct Something: Decodable {
let value: [Int]
}

because the initializer and the CodingKeys can be inferred.

With JSONDecoder in Swift 4, can missing keys use a default value instead of having to be optional properties?

Approach that I prefer is using so called DTOs - data transfer object.
It is a struct, that conforms to Codable and represents the desired object.

struct MyClassDTO: Codable {
let items: [String]?
let otherVar: Int?
}

Then you simply init the object that you want to use in the app with that DTO.

 class MyClass {
let items: [String]
var otherVar = 3
init(_ dto: MyClassDTO) {
items = dto.items ?? [String]()
otherVar = dto.otherVar ?? 3
}

var dto: MyClassDTO {
return MyClassDTO(items: items, otherVar: otherVar)
}
}

This approach is also good since you can rename and change final object however you wish to.
It is clear and requires less code than manual decoding.
Moreover, with this approach you can separate networking layer from other app.



Related Topics



Leave a reply



Submit