NSKeyedUnarchiver creates multiple instances of the same object references by others - swift

I'm experimenting with NSKeyedArchiver because I'm looking into saving an object graph. However, there seems to be a problem with objects referencing the same instance of another object. Say, I have three classes: A, B and C that reference each other like this:
A --> B <-- C
A and C each have a reference to the same object B. When decoding this using NSKeyedUnarchiver, it simply creates multiple instances of B so that A and B do not reference the same object anymore.
Here's a full example:
import Foundation
class A: Codable {
var b: B
init(b: B) {
self.b = b
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.b = try container.decode(B.self, forKey: .b)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(b, forKey: .b)
}
private enum CodingKeys: String, CodingKey {
case b
}
}
class B: Codable {
init() {}
required init(from decoder: Decoder) throws {}
func encode(to encoder: Encoder) throws {}
}
class C: Codable {
var b: B
init(b: B) {
self.b = b
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.b = try container.decode(B.self, forKey: .b)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(b, forKey: .b)
}
private enum CodingKeys: String, CodingKey {
case b
}
}
class Store: Codable {
var a: A
var b: B
var c: C
init() {
b = B()
a = A(b: b)
c = C(b: b)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.a = try container.decode(A.self, forKey: .a)
self.b = try container.decode(B.self, forKey: .b)
self.c = try container.decode(C.self, forKey: .c)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(a, forKey: .a)
try container.encode(b, forKey: .b)
try container.encode(c, forKey: .c)
}
private enum CodingKeys: String, CodingKey {
case a, b, c
}
}
let store = Store()
let archiver = NSKeyedArchiver(requiringSecureCoding: false)
try! archiver.encodeEncodable(store, forKey: NSKeyedArchiveRootObjectKey)
archiver.finishEncoding()
let unarchiver = try! NSKeyedUnarchiver(forReadingFrom: archiver.encodedData)
if let decodedStore = try! unarchiver.decodeTopLevelDecodable(Store.self, forKey: NSKeyedArchiveRootObjectKey) {
// Will print false
print(decodedStore.a.b === decodedStore.c.b)
}
Am I doing something wrong or does something like this simply not work? Or is my example flawed?
Thanks!

Related

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?).

Swift Recursively Encode a Custom Class Data Structure

I want to save a queue in UserDefault. When I decode the Queue from UserDefault, head and tail of the Queue are assigned by value which means they are two completely different objects and I can't enqueue through the tail. Is it possible to encode tail as a reference to head?
This is how class Queue is implemented
#State var RecentEmoji = Queue()
public class Node: Codable {
var value: Int
var next: Node?
init(value: Int) {
self.value = value
self.next = nil
}
enum CodingKeys: CodingKey {
case value, next
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
try container.encode(next, forKey: .next)
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
value = try container.decode(Int.self, forKey: .value)
next = try container.decode(Node?.self, forKey: .next)
}
}
public class Queue: Sequence, Codable {
fileprivate var head: Node?
private var tail: Node?
private var length: Int
init() {
head = nil
tail = nil
length = 0
}
enum CodingKeys: CodingKey {
case head, tail, length
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(head, forKey: .head)
try container.encode(tail, forKey: .tail)
try container.encode(length, forKey: .length)
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
head = try container.decode(Node?.self, forKey: .head)
tail = try container.decode(Node?.self, forKey: .tail)
length = try container.decode(Int.self, forKey: .length)
}
}
This is how I save the Queue to userDefault
let encoder = JSONEncoder()
do {
let encoded = try encoder.encode(RecentEmoji)
let defaults = UserDefaults.standard
defaults.set(encoded, forKey: "RecentEmoji")
} catch {
print("\(error)")
}
And this is how I get them back
if let savedEmoji = UserDefaults.standard.value(forKey: "RecentEmoji") as? Data {
let decoder = JSONDecoder()
if let recentEmoji = try? decoder.decode(Queue.self, from: savedEmoji) {
RecentEmoji = recentEmoji
}
}

Error while I'm trying to decode using CodingKeys

This my struct
import Foundation
struct Settings: Hashable, Decodable{
var Id = UUID()
var userNotificationId : Int
}
Coding Keys
private enum CodingKeys: String, CodingKey{
**case userNotificationId = "usuarioNotificacionMovilId"** (this is the line that gets me errors)
}
init
init(userNotificationId: Int){
self.userNotificationId = userNotificationId
}
Decoder
init(from decoder: Decoder) throws{
let container = try decoder.container(keyedBy: CodingKeys.self)
userNotificationId = try container.decodeIfPresent(Int.self, forKey: .userNotificationId) ?? 0
}
Encoder
init(from encoder: Encoder) throws{
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(userNotificationId, forKey: .userNotificationId)
}
I get the following error inside the coding method
'self' used before all stored properties are initialized
What is init(from encoder: Encoder) supposed to be? You're not conforming to Encodable, and if you were, you would need to implement func encode(to encoder: Encoder) throws, not another initializer.
That said, your explicit implementation of init(from decoder: Decoder) throws does nothing different from what the compiler would synthesize for you, so it's better to remove it entirely as well.
struct Settings: Hashable, Decodable {
let id = UUID()
let userNotificationId: Int
private enum CodingKeys: String, CodingKey{
case userNotificationId = "usuarioNotificacionMovilId"
}
init(userNotificationId: Int) {
self.userNotificationId = userNotificationId
}
}
is probably all you need.

Polymorphically decode a sub-object of a KeyedDecodingContainer in Swift

The following code attempts to create a Codable type AnyBase that would allow you to encode and decode various subclasses of Base. As written, in fails, because it tries to lookup the type objects by a string code, using getTypeFromCode. But if you use the commented-out part instead with hard-coded types, it prints "Sub1", as desired.
Is there a way to adjust this to use something like getTypeFromCode and eliminate the hard-coded types from init?
class Base: Codable {
var foo: Int = 1
var codingTypeKey: String { fatalError("abstract") }
}
class Sub1: Base {
var sub1Prop: Int = 2
override var codingTypeKey: String { "sub1" }
enum CodingKeys: CodingKey {
case sub1Prop
}
override init() {
super.init()
}
required init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
sub1Prop = try c.decode(Int.self, forKey: .sub1Prop)
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(sub1Prop, forKey: .sub1Prop)
try super.encode(to: encoder)
}
}
class Sub2: Base {
var sub2Prop: Int = 2
override var codingTypeKey: String { "sub2" }
}
func getTypeFromCode(_ typeCode: String) -> Base.Type {
if typeCode == "sub1" { return Sub1.self }
else if typeCode == "sub2" { return Sub2.self }
else { fatalError("bad type code") }
}
class AnyBase: Codable {
let base: Base
init(_ b: Base) { base = b }
required init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
let typeCode = try c.decode(String.self, forKey: .type)
/*if typeCode == "sub1" {
self.base = try c.decode(Sub1.self, forKey: .base)
} else if typeCode == "sub2" {
self.base = try c.decode(Sub2.self, forKey: .base)
} else {
fatalError("bad type code")
}*/
let typeObj = getTypeFromCode(typeCode)
self.base = try c.decode(typeObj, forKey: .base)
}
func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encode(base.codingTypeKey, forKey: .type)
try c.encode(base, forKey: .base)
}
enum CodingKeys: CodingKey {
case base, type
}
}
// To to round-trip encode/decode and get the right object back...
let enc = JSONEncoder()
let dec = JSONDecoder()
let sub = Sub1()
let any = AnyBase(sub)
let data = try! enc.encode(any)
let str = String(data:data, encoding:.utf8)!
print(str)
let any2 = try! dec.decode(AnyBase.self, from: data)
print(type(of:any2.base))
Instead of getTypeFromCode (_:) method, you can create an enum BaseType like,
enum BaseType: String {
case sub1, sub2
var type: Base.Type {
switch self {
case .sub1: return Sub1.self
case .sub2: return Sub2.self
}
}
}
Now, in init(from:) get the BaseType using typeCode as rawValue, i.e.
class AnyBase: Codable {
required init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
let typeCode = try c.decode(String.self, forKey: .type)
if let baseType = BaseType(rawValue: typeCode) {
self.base = try c.decode(baseType.type, forKey: .base)
} else {
fatalError("bad type code")
}
}
//rest of the code...
}

Codable conformance for nested Enum?

I'm a bit tangled up on using Codable with nested enums. Suppose I have the following enum:
enum ItemStatus {
enum Foo {
case a
case b(Double, Double)
case c
}
enum Bar {
case x(String)
case y
case z(Int)
}
}
extension ItemStatus: Codable {
init(from decoder: Decoder) throws {
}
func encode(to encoder: Encoder) throws {
}
}
For normal enums, I'm implementing Codable like this. It works, but I'm not sure how to implement Codable for the top-level enum (in this case, ItemStats), and my gut tells me there is a more efficient way to write this anyway:
extension ItemStatus.Bar {
init?(rawValue: String?) {
guard let val = rawValue?.lowercased() else {
return nil
}
switch val {
case "x": self = .x("")
case "y": self = .y
case "z": self = .z(0)
default: return nil
}
}
private var typeStr: String {
guard let label = Mirror(reflecting: self).children.first?.label else {
return .init(describing: self)
}
return label
}
}
extension ItemStatus.Bar : Codable {
private enum CodingKeys: String, CodingKey {
case type
case xVal
case zVal
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard let type = try? container.decode(String.self, forKey: .type) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Could not get type of Bar."))
}
guard let base = ItemStatus.Bar(rawValue: type) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Could not init Bar with value: \(type)"))
}
switch base {
case .x:
let val = try container.decode(String.self, forKey: .xVal)
self = .x(val)
case .z:
let val = try container.decode(Int.self, forKey: .zVal)
self = .z(val)
default:
self = base
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.typeStr, forKey: .type)
switch self {
case .x(let val):
try container.encode(val, forKey: .xVal)
case .z(let val):
try container.encode(val, forKey: .zVal)
default: ()
}
}
}