diff --git a/Sources/Leaf/LeafEncoder.swift b/Sources/Leaf/LeafEncoder.swift index 2584769..3cd7fa8 100644 --- a/Sources/Leaf/LeafEncoder.swift +++ b/Sources/Leaf/LeafEncoder.swift @@ -136,8 +136,9 @@ extension LeafEncoder { } private final class KeyedContainerImpl: KeyedEncodingContainerProtocol, LeafEncodingResolvable where Key: CodingKey { - private let encoder: EncoderImpl + private weak var encoder: EncoderImpl! private var data: [String: LeafEncodingResolvable] = [:] + private var nestedEncoderCaptures = [AnyObject]() /// See ``LeafEncodingResolvable/resolvedData``. var resolvedData: LeafData? { .dictionary(self.data.compactMapValues { $0.resolvedData }) } @@ -158,10 +159,17 @@ extension LeafEncoder { /// See ``KeyedEncodingContainerProtocol/nestedContainer(keyedBy:forKey:)``. func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer { + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + let nestedEncoder = EncoderImpl(from: encoder, withKey: key) + nestedEncoderCaptures.append(nestedEncoder) + /// Use a subencoder to create a nested container so the coding paths are correctly maintained. /// Save the subcontainer in our data so it can be resolved later before returning it. - .init(self.insert( - EncoderImpl(from: self.encoder, withKey: key).rawContainer(keyedBy: NestedKey.self), + return .init(self.insert( + nestedEncoder.rawContainer(keyedBy: NestedKey.self), forKey: key, as: KeyedContainerImpl.self )) @@ -169,8 +177,15 @@ extension LeafEncoder { /// See ``KeyedEncodingContainerProtocol/nestedUnkeyedContainer(forKey:)``. func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { - self.insert( - EncoderImpl(from: self.encoder, withKey: key).unkeyedContainer() as! UnkeyedContainerImpl, + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + let nestedEncoder = EncoderImpl(from: encoder, withKey: key) + nestedEncoderCaptures.append(nestedEncoder) + + return self.insert( + nestedEncoder.unkeyedContainer() as! UnkeyedContainerImpl, forKey: key ) } @@ -178,15 +193,23 @@ extension LeafEncoder { /// A super encoder is, in fact, just a subdecoder with delusions of grandeur and some rather haughty /// pretensions. (It's mostly Codable's fault anyway.) func superEncoder() -> Encoder { - self.insert( - EncoderImpl(from: self.encoder, withKey: GenericCodingKey(stringValue: "super")), + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + return self.insert( + EncoderImpl(from: encoder, withKey: GenericCodingKey(stringValue: "super")), forKey: GenericCodingKey(stringValue: "super") ) } /// See ``KeyedEncodingContainerProtocol/superEncoder(forKey:)``. func superEncoder(forKey key: Key) -> Encoder { - self.insert(EncoderImpl(from: self.encoder, withKey: key), forKey: key) + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + return self.insert(EncoderImpl(from: encoder, withKey: key), forKey: key) } /// Helper for the encoding methods. @@ -197,9 +220,11 @@ extension LeafEncoder { } private final class UnkeyedContainerImpl: UnkeyedEncodingContainer, LeafEncodingResolvable { - private let encoder: EncoderImpl + private weak var encoder: EncoderImpl! private var data: [LeafEncodingResolvable] = [] + private var nestedEncoderCaptures = [AnyObject]() + /// See ``LeafEncodingResolvable/resolvedData``. var resolvedData: LeafData? { .array(data.compactMap(\.resolvedData)) } @@ -224,20 +249,38 @@ extension LeafEncoder { /// See ``UnkeyedEncodingContainer/nestedContainer(keyedBy:)``. func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer { - .init(self.add( - EncoderImpl(from: self.encoder, withKey: self.nextCodingKey).rawContainer(keyedBy: NestedKey.self), + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + let nestedEncoder = EncoderImpl(from: encoder, withKey: self.nextCodingKey) + nestedEncoderCaptures.append(nestedEncoder) + + return .init(self.add( + nestedEncoder.rawContainer(keyedBy: NestedKey.self), as: KeyedContainerImpl.self )) } /// See ``UnkeyedEncodingContainer/nestedUnkeyedContainer()``. func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - self.add(EncoderImpl(from: self.encoder, withKey: self.nextCodingKey).unkeyedContainer() as! UnkeyedContainerImpl) + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + let nestedEncoder = EncoderImpl(from: encoder, withKey: self.nextCodingKey) + nestedEncoderCaptures.append(nestedEncoder) + + return self.add(nestedEncoder.unkeyedContainer() as! UnkeyedContainerImpl) } /// See ``UnkeyedEncodingContainer/superEncoder()``. func superEncoder() -> Encoder { - self.add(EncoderImpl(from: self.encoder, withKey: self.nextCodingKey)) + guard let encoder = encoder else { + fatalError("Encoder deallocated") + } + + return self.add(EncoderImpl(from: encoder, withKey: self.nextCodingKey)) } /// A `CodingKey` corresponding to the index that will be given to the next value added to the array.