I have struct
that conforms to protocol Codable
. Some properties are decoded from JSON fields so I use the CodingKeys
enum for that. But there are few properties that are not in my JSON and I need to calculate them from decoded JSON properties. For example, if you get a Zip code
from JSON, I want to calculate City
from it.
I don't want City
to be an optional String. So I try to calculate it right after my Zip code
field is decoded from JSON.
struct Place: Codable {
var name: String
var zipcode: String
// ... Lot of other properties decoded from JSON
var city: String // This property has to be calulated after `zip code` is decoded
enum CodingKeys: String, CodingKey {
case name = "placeName"
case zipcode = "NPA"
// other properties from JSON
}
}
I've tried this solution to rewrite init(from decoder: Decoder)
. But that means I need to manually write each property I need to decode. As I have a lot, I would prefer to let default init decoder does it job, and then add my code to calculate City
.
Is there a way to do something like : call default init with decoder, then add some code ?
I was also thinking about computed property. But as calculating City
from Zip code is quite lot of code, I don't want that it is always computed.
I need something like :
init(from decoder: Decoder) throws {
// <- Call default init from decoder
city = CityHelper(from: zipcode) // quite heavy code in there
}
CodePudding user response:
I would prefer to let default init decoder does it job, and then add my code to calculate City
Unfortunately you can't. It is currently all or nothing; you cannot treat the synthesized init as some sort of inheritance from super (as in your imagined Call default init
).
I was also thinking about computed property. But as calculating City from Zip code is quite lot of code, I don't want that it is always computed.
Use a lazy var property whose initializer calls a method that transforms zip to city. That way it is calculated, but just once. The zip will not change, so this is an acceptable compromise.
Or even better, use a reducer to transform the decoded struct (with zip) into a completely different struct (with city).
CodePudding user response:
Ok misread at first. Here's something I think could help: use willSet or didSet on zip to then compute the city perhaps? Not sure if that gets you around not having a default value but this code will only run if the zipcode changes
struct Place: Codable {
var name: String
var zipcode: String{
didSet{
//do something after zip is set like calculate city?
}
willSet{
//do something before zip is set.
}
}
// ... Lot of other properties decoded from JSON
var city: String // This property has to be calulated after `zip code` is decoded
enum CodingKeys: String, CodingKey {
case name = "placeName"
case zipcode = "NPA"
// other properties from JSON
}
}
CodePudding user response:
You can use a computed property that relies on the zip to return a city. So you'd have to figure out the logic of how to map zip back to city with some method but here's a simple example I cooked up:
struct Rectangle : Codable{
var width : Float
var height : Float
var area : Float{
return width * height
}
}
Usage:
let myRect = Rectangle(width: 5.0, height: 5.0)
print(myRect.area)
Result: 25.0