Swift 4 JSONDecoder optional variable
If you have local variables you have to specify the CodingKeys
public struct VIO: Codable {
private enum CodingKeys : String, CodingKey { case id }
let id:Int?
...
var par1:Bool = false
var par2:Bool = false
}
Edit:
If par1
and par2
should be also decoded optionally you have to write a custom initializer
private enum CodingKeys : String, CodingKey { case id, par1, par2 }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
par1 = try container.decodeIfPresent(Bool.self, forKey: .par1)
par2 = try container.decodeIfPresent(Bool.self, forKey: .par2)
}
This is Swift: No trailing semicolons
Decoding optional values in JSON - Swift 4
The API decodeIfPresent
is pointless in this case because the key does exist.
An existing key with null
value and a missing key are two different situations.
And never force unwrap in the init(from decoder
method.
There are two options:
- Delete the entire
init
method then the optional struct members handle thenull
values. Use regular
decode
and ignore the errorname = try? values.decode(String.self, forKey: .name)
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.
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.
Decoding json with optional fields
You should split this into several steps in order to avoid to handle all these optionals in your model.
First create a struct that has only those properties that are guaranteed to be there. ok
in your case:
struct OKResult: Codable{
let ok: Bool
}
then create one for your error state and one for your success state:
struct ErrorResult: Codable{
let ok: Bool
let errorCode: Int
let error: String
private enum CodingKeys: String, CodingKey{
case ok, errorCode = "error_code", error
}
}
struct ShortLinkData: Codable {
let ok: Bool
let result: Result
}
struct Result: Codable {
let code, shortLink: String
let fullShortLink: String
let shortLink2: String
let fullShortLink2: String
let shortLink3: String
let fullShortLink3: String
let shareLink: String
let fullShareLink: String
let originalLink: String
enum CodingKeys: String, CodingKey {
case code
case shortLink = "short_link"
case fullShortLink = "full_short_link"
case shortLink2 = "short_link2"
case fullShortLink2 = "full_short_link2"
case shortLink3 = "short_link3"
case fullShortLink3 = "full_short_link3"
case shareLink = "share_link"
case fullShareLink = "full_share_link"
case originalLink = "original_link"
}
}
Then you can decode the data:
guard try JSONDecoder().decode(OKResult.self, from: data).ok else{
let errorResponse = try JSONDecoder().decode(ErrorResult.self, from: data)
//handle error scenario
fatalError(errorResponse.error) // or throw custom error or return nil etc...
}
let shortlinkData = try JSONDecoder().decode(ShortLinkData.self, from: data)
Remarks:
- Your
init
s are not necessary. - Never use
try?
this will hide all errors from you - you would need to wrap this either in a
do catch
block or make your functionthrowing
and handle errors further up the tree.
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)
}
Related Topics
Problem with Swiftui and Foreach on Xcode Playground
Swift: Simple Dispatchqueue Does Not Run & Notify Correctly
How to Dynamically Hide Navigation Back Button in Swiftui
Rxswift + Mvvm + Coordinator Pattern, How to Wait for Coordinator Result Sequentially
Root Class of All Classes in Swift
Testing a Class Which Preserves Its State in Private Variables
Swift: Object Instance by Name
Drawing at Cocoa with Swift Creates an Error
How to Resize an Image That Is Being Printed
How to Pass Text from Cell to Textview in Another View Controller
Why Swift Disallows Weak Reference for Non-Optional Type
How to Reconstruct Grayscale Image from Intensity Values
Can't Set @Ibinspectable Computed Property in UIview
Appdelegate#Applicationdidfinishlaunching Not Called for Swift 4 Macos App Built from Command Line
Swift Equivalent of Objective-C for Flutter Native Ads
List Is Not Conforming to Encodable