|
// ==============================================================================================| |
|
// | | |
|
// | http://pointfree.co/blog/posts/78-reverse-engineering-swiftui-s-navigationpath-codability | |
|
// | | |
|
// ==============================================================================================| |
|
|
|
import Foundation |
|
|
|
public struct NavPath { |
|
var elements: [Any] |
|
public init(_ elements: [Any] = []) { |
|
self.elements = elements |
|
} |
|
|
|
public mutating func append(_ newElement: Any) { |
|
self.elements.append(newElement) |
|
} |
|
|
|
public var isEmpty: Bool { |
|
self.elements.isEmpty |
|
} |
|
|
|
mutating public func removeLast(_ k: Int) { |
|
self.elements.removeLast(k) |
|
} |
|
} |
|
|
|
extension NavPath: Encodable { |
|
public func encode(to encoder: Encoder) throws { |
|
var container = encoder.unkeyedContainer() |
|
for element in elements.reversed() { |
|
try container.encode(_mangledTypeName(type(of: element))) |
|
guard let element = element as? any Encodable |
|
else { |
|
throw EncodingError.invalidValue( |
|
element, |
|
.init( |
|
codingPath: container.codingPath, |
|
debugDescription: "\(type(of: element)) is not encodable." |
|
) |
|
) |
|
} |
|
|
|
#if swift(<5.7) |
|
func open<A: Encodable>(_: A.Type) throws -> Data { |
|
try JSONEncoder().encode(element as! A) |
|
} |
|
let string = try String( |
|
decoding: _openExistential(type(of: element), do: open), |
|
as: UTF8.self |
|
) |
|
try container.encode(string) |
|
#else |
|
let string = try String(decoding: JSONEncoder().encode(element), as: UTF8.self) |
|
try container.encode(string) |
|
#endif |
|
} |
|
} |
|
} |
|
|
|
extension NavPath: Decodable { |
|
public init(from decoder: Decoder) throws { |
|
var container = try decoder.unkeyedContainer() |
|
self.elements = [] |
|
while !container.isAtEnd { |
|
let typeName = try container.decode(String.self) |
|
guard let type = _typeByName(typeName) as? any Decodable.Type |
|
else { |
|
throw DecodingError.dataCorruptedError( |
|
in: container, |
|
debugDescription: "\(typeName) is not decodable." |
|
) |
|
} |
|
let encodedValue = try container.decode(String.self) |
|
#if swift(<5.7) |
|
func decode<A: Decodable>(_: A.Type) throws -> A { |
|
try JSONDecoder().decode(A.self, from: Data(encodedValue.utf8)) |
|
} |
|
let value = try _openExistential(type, do: decode) |
|
#else |
|
let value = try JSONDecoder().decode(type, from: Data(encodedValue.utf8)) |
|
#endif |
|
self.elements.insert(value, at: 0) |
|
} |
|
} |
|
} |