Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Decodes multiple layers of nested containers using a String array for key path. (It's actually cleaner to declare another struct for the nested data. But this is to show how this can be done.)
import Foundation
// Allows defining CodingKey from String
struct DynamicCodingKey: CodingKey {
var intValue: Int?
var stringValue: String
init?(intValue: Int) {
assertionFailure("Not implemented")
return nil
}
init?(stringValue: String) {
self.stringValue = stringValue
}
}
extension KeyedDecodingContainer where Key == DynamicCodingKey {
// Decodes multiple layers of nested containers using a String array for key path.
func decode<T: Decodable>(_ type: T.Type, forKeyPath keys: [String]) throws -> T? {
var container: KeyedDecodingContainer = self
for key in keys {
guard let realKey = container.allKeys.first(where: { $0.stringValue == key }) else { return nil }
if key == keys.last {
return try container.decode(T.self, forKey: realKey)
}
else {
container = try container.nestedContainer(keyedBy: Key.self, forKey: realKey)
}
}
return nil
}
}
import Foundation
@testable import Viki
import XCTest
class DynamicCodingKeyTests: XCTestCase {
private struct Foo: Decodable {
let teamName: String?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DynamicCodingKey.self)
self.teamName = try container.decode(String.self, forKeyPath: ["team", "name"])
}
}
func testDefaultSettingsFileNotFound() throws {
let data = """
{
"team": { "name": "foo" }
}
""".data(using: .utf8)!
let output = try JSONDecoder().decode(Foo.self, from: data)
XCTAssertEqual(output.teamName, "foo")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment