Last active
March 6, 2020 15:27
-
-
Save KingOfBrian/8712d5f2ccd169dc042c6eed8c6f46d9 to your computer and use it in GitHub Desktop.
Fix encoding of key wrapper types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Codable doesn't do a good job of Dictionaries with non-String keys. | |
/// This property wrapper fixes things. | |
/// | |
/// Context: https://oleb.net/blog/2017/12/dictionary-codable-array/ | |
/// Tests: StringKeyDictionaryTests | |
@propertyWrapper | |
public struct StringKeyDictionary<Key: RawRepresentable & Hashable & Decodable, Value: Codable>: Codable where Key.RawValue == String { | |
public var wrappedValue: [Key: Value] | |
public init(wrappedValue: [Key: Value]) { | |
self.wrappedValue = wrappedValue | |
} | |
public init(from decoder: Decoder) throws { | |
let value = try [String: Value](from: decoder) | |
let tuples = value.compactMap { (key, value) -> (Key, Value)? in | |
return Key(rawValue: key).flatMap { ($0, value) } | |
} | |
self.wrappedValue = Dictionary(tuples, uniquingKeysWith: { key, _ in key }) | |
} | |
public func encode(to encoder: Encoder) throws { | |
let tuples = wrappedValue.map { (key, value) -> (String, Value) in | |
return (key.rawValue, value) | |
} | |
try Dictionary(tuples, uniquingKeysWith: { key, _ in key }) | |
.encode(to: encoder) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class StringKeyDictionaryTests: XCTestCase { | |
private enum KeyType: String, Codable { | |
case test, keys | |
} | |
private struct BrokenKeyType: Codable { | |
let values: [KeyType: String] | |
} | |
private struct BrokenProof: Codable { | |
let values: [String] | |
} | |
private struct FixedKeyType: Codable { | |
@StringKeyDictionary | |
var values: [KeyType: String] | |
} | |
private struct FixedProof: Codable { | |
let values: [String: String] | |
} | |
func testBroken() throws { | |
let broken = BrokenKeyType(values: [ | |
.test: "Test", | |
.keys: "Keys", | |
]) | |
let data = try JSONEncoder().encode(broken) | |
let wat = try JSONDecoder().decode(BrokenProof.self, from: data) | |
XCTAssertEqual(wat.values.sorted(), ["test", "Test", "keys", "Keys"].sorted()) | |
} | |
func testFixed() throws { | |
let broken = FixedKeyType(values: [ | |
.test: "Test", | |
.keys: "Keys", | |
]) | |
let data = try JSONEncoder().encode(broken) | |
let lols = try JSONDecoder().decode(FixedProof.self, from: data) | |
XCTAssertEqual(lols.values, ["test": "Test", "keys": "Keys"]) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment