can't that be asserted at runtime, just like how we assert expectations of types when decoding?
For future readers, here's my solution:
import Foundation
struct UserDefaultsDomain: Hashable {
var domain: String
init(_ domain: String) { self.domain = domain }
}
extension UserDefaultsDomain: CodingKey {
init(stringValue: String) { self.init(stringValue) }
var stringValue: String { self.domain }
init?(intValue: Int) { nil }
var intValue: Int? { nil }
}
extension UserDefaultsDomain: Codable {
init(from decoder: Decoder) throws {
self.init(try decoder
.singleValueContainer()
.decode(String.self)
)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(domain)
}
}
struct UserDefaultsConfigurations {
var configurations: [UserDefaultsDomain: [String: String]]
}
extension UserDefaultsConfigurations: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: UserDefaultsDomain.self)
self.configurations = Dictionary(uniqueKeysWithValues:
container.allKeys.lazy.map { domain in
let values = try! container.decode([String: String].self, forKey: domain)
return (key: domain, value: values)
}
)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: UserDefaultsDomain.self)
for (domain, values) in self.configurations {
try container.encode(values, forKey: domain)
}
}
}
let testData = UserDefaultsConfigurations(configurations: [
UserDefaultsDomain("a.b.c"): [
"key1": "value1",
"key2": "value2",
"key3": "value3",
],
UserDefaultsDomain("com.example"): [
"key1": "value1",
"key2": "value2",
"key3": "value3",
],
UserDefaultsDomain("x.y.z"): [
"key1": "value1",
"key2": "value2",
"key3": "value3",
],
])
// Encode to JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(testData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
// decode back
let decoder = JSONDecoder()
let decoded = try decoder.decode(UserDefaultsConfigurations.self, from: jsonData)
dump(decoded)
do { // and back to JSON!
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(decoded)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
}