Swift recursive struct in db cannot solved Codable compliant - swift

Swift recursive struct can be solved by helper struct as given below. Unfortunately, this is not Codable compliant:
class Box<T> {
let boxed: T
init(_ thingToBox: T) { boxed = thingToBox }
}
struct ContainerClass: Identifiable, Codable {
let id: UUID?
var classname: String
var parent: Box<ContainerClass>?
private enum ContainerClassKeys: String, CodingKey {
case id
case classname
case parent
}
}
Error: Type 'ContainerClass' does not conform to protocol 'Encodable'

You must make Box conform to Codable.
You can use conditional conformance to only make Box Codable when its generic type T is Codable as well.
final class Box<T> {
let boxed: T
init(_ thingToBox: T) { boxed = thingToBox }
}
struct ContainerClass: Identifiable, Codable {
let id: UUID?
var classname: String
var parent: Box<ContainerClass>?
private enum ContainerClassKeys: String, CodingKey {
case id
case classname
case parent
}
}
extension Box: Codable where T: Codable {
convenience init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let boxed = try container.decode(T.self)
self.init(boxed)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(boxed)
}
}
Alternatively, if you always want T to be Codable, you can simplify the code to
class Box<T: Codable>: Codable {
let boxed: T
init(_ thingToBox: T) { boxed = thingToBox }
}

Related

Set struct var in a nested enum if fail to decode

I have a simple struct that I need to set a flag on enum decode failure. Not sure now to do that.
struct TestStruct: Codable {
let testEnum: TestEnum
let failDecode: Bool
}
extension TestStruct {
enum TestEnum: String, Codable {
case val1 = "VAL1"
case val2 = "VAL2"
public init(from decoder: Decoder) throws {
let testState = try decoder.singleValueContainer().decode(String.self)
switch testState{
case "VAL1": self = .val1
case "VAL2": self = .val2
default: TestStruct.failDecode = true // fail compile here
}
}
}
}
How do I set failDecode flag on decode failure? Thxs
You need a custom init(from:) initializer for TestStruct. You do not need a custom init(from:) initializer for TestEnum.
struct TestStruct {
let testEnum: TestEnum
let failDecode: Bool
}
extension TestStruct {
enum TestEnum: String, Codable {
case val1 = "VAL1"
case val2 = "VAL2"
}
}
extension TestStruct: Decodable {
public init(from decoder: Decoder) throws {
do {
testEnum = try TestEnum(from: decoder)
failDecode = false
} catch {
failDecode = true
// You must initialize testEnum to some valid value,
// even though you couldn't decode it!
testEnum = .val1
}
}
}
If you want Codable conformance, not just Decodable conformance, then you also need to provide a matching encode(to:) method. Here's a simple implementation:
extension TestStruct: Encodable {
func encode(to encoder: Encoder) throws {
try testEnum.encode(to: encoder)
}
}
(Note that Codable is an alias for Decodable & Encodable, so providing separate Decodable and Encodable conformances is the same as providing a single Codable conformance.)
I ignored the failDecode flag in that implementation of encode(to:), but maybe you want to encode an illegal value if failDecode is set. This version encodes nil if failDecode is true:
extension TestStruct: Encodable {
func encode(to encoder: Encoder) throws {
if failDecode {
try Optional<TestEnum>.none.encode(to: encoder)
} else {
try testEnum.encode(to: encoder)
}
}
}

How swift implement the default Decodable for struct?

struct Person: Decodable {
let firstName: String
}
var data = """
{"firstName": "Fai"}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let parsed = try decoder.decode(Person.self, from: data)
JSONDecoder will decode the data, which is comfirmed to Decodable protocol.
So I want to know how swift implement this. But I can not get any idea in the source code: https://github.com/apple/swift/blob/main/stdlib/public/core/Codable.swift
The Decodable protocol only need to implement an init(from decoder: Decoder) function.
If I am going to do it, I will make an extension for struct:
extension struct: Decodable {
init(from decoder: Decoder) {...}
}
But when I delete the Decodable on my example, the compiler give errors:
Instance method 'decode(_:from:)' requires that 'Person' conform to 'Decodable'
So this is not the swift way to implement this. How's swift way? And where's the source code?
You can see what the compiler writes for you using -print-ast:
echo 'struct Person: Decodable {
let firstName: String
}' | swiftc -print-ast -
This will output most of the auto-generated code (it should include all of the Codable conformances, but there are a few other kinds of auto-generated code that won't include their implementation):
internal struct Person : Decodable {
internal let firstName: String
private enum CodingKeys : CodingKey {
case firstName
#_implements(Equatable, ==(_:_:)) fileprivate static func __derived_enum_equals(_ a: Person.CodingKeys, _ b: Person.CodingKeys) -> Bool {
private var index_a: Int
switch a {
case .firstName:
index_a = 0
}
private var index_b: Int
switch b {
case .firstName:
index_b = 0
}
return index_a == index_b
}
fileprivate func hash(into hasher: inout Hasher) {
private var discriminator: Int
switch self {
case .firstName:
discriminator = 0
}
hasher.combine(discriminator)
}
private init?(stringValue: String) {
switch stringValue {
case "firstName":
self = Person.CodingKeys.firstName
default:
return nil
}
}
private init?(intValue: Int) {
return nil
}
fileprivate var hashValue: Int {
get {
return _hashValue(for: self)
}
}
fileprivate var intValue: Int? {
get {
return nil
}
}
fileprivate var stringValue: String {
get {
switch self {
case .firstName:
return "firstName"
}
}
}
}
internal init(firstName: String)
internal init(from decoder: Decoder) throws {
#_hasInitialValue private let container: KeyedDecodingContainer<Person.CodingKeys> = try decoder.container(keyedBy: Person.CodingKeys.self)
self.firstName = try container.decode(String.self, forKey: Person.CodingKeys.firstName)
}
}
For the full implementation details, see DerivedConformanceCodable.cpp. Probably of most interest to your question is deriveBodyDecodable_init.

Swift struct with custom encoder and decoder cannot conform to 'Encodable'

[Edited to provide a minimal reproducible example ]
This is the complete struct without non relevant vars and functions
InstrumentSet.swift
import Foundation
struct InstrumentsSet: Identifiable, Codable {
private enum CodingKeys: String, CodingKey {
case name = "setName"
case tracks = "instrumentsConfig"
}
var id: String { name }
var name: String
var tracks: [Track]
}
Track.swift
import Foundation
extension InstrumentsSet {
struct Track: Identifiable, Encodable {
private enum TrackKeys: String, CodingKey {
case id = "trackId"
case effects
}
let id: String
var effects: [Effect]?
}
}
extension InstrumentsSet.Track: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: TrackKeys.self)
id = try container.decode(String.self, forKey: .id)
effects = try container.decodeIfPresent([Effect].self, forKey: .effects)
}
}
Effect.swift
import Foundation
import AudioKit
import SoundpipeAudioKit
extension InstrumentsSet.Track {
enum Effect: Decodable {
private enum EffectKeys: String, CodingKey {
case effectType = "effectName"
case cutoffFrequency
case resonance
}
case lowPassFilter(LowPassFilterEffect)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: EffectKeys.self)
let effectType = try container.decode(Effect.EffectType.self, forKey: .effectType)
switch effectType {
case .lowPassFilter:
let cutOffFrequency = try container.decode(ValueAndRange.self, forKey: .cutoffFrequency)
let resonance = try container.decode(ValueAndRange.self, forKey: .resonance)
self = .lowPassFilter(LowPassFilterEffect(cutOffFrequency: cutOffFrequency, resonance: resonance))
default:
fatalError("Not implemented!")
}
}
}
}
extension InstrumentsSet.Track.Effect {
enum EffectType: String, Decodable {
case lowPassFilter
}
}
extension InstrumentsSet.Track.Effect: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: EffectKeys.self)
//FIXME: This is the location of the error: Type 'ValueAndRange.Type' cannot conform to 'Encodable'
try container.encode(ValueAndRange.self, forKey: .cutoffFrequency)
}
}
The problem is ValueAndRange.self not not conforming to Encodable
I've followed multiple examples to get to this implementation:
import Foundation
import AudioKit
struct ValueAndRange: Encodable {
private enum ValueRangeKeys: String, CodingKey {
case value
case range
}
static var zero: ValueAndRange { .init(value: 0, range: [0, 0]) }
var value: AUValue
var range: [Double]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ValueRangeKeys.self)
try container.encode(value, forKey: .value)
try container.encode(range, forKey: .range)
}
}
extension ValueAndRange: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ValueRangeKeys.self)
value = try container.decode(AUValue.self, forKey: .value)
range = try container.decode([Double].self, forKey: .range)
}
}
I cannot see why this struct should not conform to Encodable. Maybe any of you got betters eyes (and brains) then I got?
Your encode function is incorrectly trying to encode a type, ValueAndRange.self, rather than a value.
Looking at the init(from:) method I think your encode function should look something like this
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: EffectKeys.self)
switch self {
case .lowPassFilter(let effect):
try container.encode(effect.cutOffFrequency, forKey: .cutoffFrequency)
try container.encode(effect.resonance, forKey: .resonance)
}
}
I didn't include .effectType in this code since I am uncertain of its usage (isn't it always the same hard coded string?).

Automatic decodable synthesis for decodable property wrappers

Let's say I have decodable property wrapper:
#propertyWrapper
struct OptionalDecodable<Value: Decodable>: Decodable {
var wrappedValue: Value?
}
The compiler does synthesize init for the following
struct Model: Decodable {
#OptionalDecodable private(set) var string: String?
}
To test if this works I just try to decode empty JSON (i.e. "{}")
However, string property is not treated as optional, i.e. when there's no string key I get an error that key was not found.
Is there a work around this?
I'm not sure if this is the best way, but the issue is that wrappedValue type of the property wrapper has to match the property's type, and String is different than String?.
One approach to overcome this is to make the property wrapper generic, but constrain in such a way that would allow you to initialize the type from a String or an Int:
protocol ExpressibleByString {
init(fromString: String)
}
extension String: ExpressibleByString {
init(fromString: String) { self = fromString }
}
extension Optional: ExpressibleByString where Wrapped == String {
init(fromString: String) { self = fromString }
}
#propertyWrapper
struct IntOrString<V: ExpressibleByString & Decodable>: Decodable {
var wrappedValue: V
}
extension IntOrString {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let int = try container.decode(Int.self)
wrappedValue = .init(fromString: int.description)
} catch DecodingError.typeMismatch {
wrappedValue = try .init(fromString: container.decode(String.self))
}
}
}
extension KeyedDecodingContainer {
func decode<V: ExpressibleByNilLiteral>(_ t: IntOrString<V>.Type, forKey key: K) throws -> IntOrString<V> {
if let v = try decodeIfPresent(t, forKey: key) {
return v
}
return IntOrString(wrappedValue: nil)
}
}
Then you could use it on both optional and non-optional String:
struct Foo: Decodable {
#IntOrString
var p1: String?
#IntOrString
var p2: String
}

Swift Decode and Encode Custom Types

I'm having difficulties decoding and encoding one of my classes in Swift. I have tried following the Encoding and Decoding Custom Types documentation but with no luck.
My class layout is as follows:
public struct MapLocation: Identifiable, Codable {
#DocumentID public var id: String?
let originLocation: [MapLandmark]
let destinationLocation: [MapLandmark]
enum CodingKeys: String, CodingKey {
case originLocation
case destinationLocation
}
}
import Foundation
import MapKit
struct MapLandmark: Codable {
let placemark: MKPlacemark
var id: UUID {
return UUID()
}
var name: String {
self.placemark.name ?? ""
}
var title: String {
self.placemark.title ?? ""
}
var coordinate: CLLocationCoordinate2D {
self.placemark.coordinate
}
}
I have tried adding the encoding and decoding classes from the above link but keep running into errors. What is the best way to implement a solution?
EDIT:
I forgot to mention the errors I am getting are:
"Type 'MapLandmark' does not conform to protocol 'Decodable'"
"Type 'MapLandmark' does not conform to protocol 'Encodable"
MKPlacemark conforms to NSSecureCoding. You can just use NSKeyedArchiver and NSKeyedUnarchiver to encode/decode it. UUID already conforms to Codable. Try as follow:
import MapKit
extension NSSecureCoding {
func archived() throws -> Data {
try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
}
}
extension Data {
func unarchived<T: NSSecureCoding>() throws -> T? {
try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(self) as? T
}
}
struct MapLandmark: Codable {
let placemark: MKPlacemark
let id: UUID
func encode(to encoder: Encoder) throws {
var unkeyedContainer = encoder.unkeyedContainer()
try unkeyedContainer.encode(placemark.archived())
try unkeyedContainer.encode(id)
}
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
placemark = try container.decode(Data.self).unarchived()!
id = try container.decode(UUID.self)
}
}