Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Sources/Decoder/DictionaryComponentDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ extension DictionaryComponentDecoder {

return data

case .blob:
return try decodePrimitiveValue(from: component, at: codingPath)

case .custom(let closure):
return try decodeCustomizedValue(from: component, at: codingPath, closure: closure)
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/Decoder/Options/DictionaryDataDecodingStrategy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public enum DictionaryDataDecodingStrategy: Sendable {
/// The strategy that decodes data using Base 64 decoding.
case base64

/// The strategy that takes data stored in the dictionary as is.
case blob

/// The strategy that decodes data using a user-defined function.
case custom(@Sendable (_ decoder: Decoder) throws -> Data)
}
3 changes: 3 additions & 0 deletions Sources/Encoder/DictionaryComponentEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ extension DictionaryComponentEncoder {
case .base64:
return encodePrimitiveValue(data.base64EncodedString(), at: codingPath)

case .blob:
return encodePrimitiveValue(data, at: codingPath)

case let .custom(closure):
return try encodeCustomizedValue(data, at: codingPath, closure: closure)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public enum DictionaryDataEncodingStrategy: Sendable {

case deferredToData
case base64
case blob
case custom(@Sendable (_ data: Data, _ encoder: Encoder) throws -> Void)
}
32 changes: 32 additions & 0 deletions Tests/Decoder/DictionaryDecoderStrategiesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,38 @@ final class DictionaryDecoderStrategiesTests: XCTestCase, DictionaryDecoderTesti
}
}

func testThatDecoderSucceedsWhenDecodingDataToBlob() {
decoder.dataDecodingStrategy = .blob

let dictionary: [String: Any] = [
"foo": Data([1, 2, 3]),
"bar": NSData(data: Data([1, 2, 3]))
]

let value = [
"foo": Data([1, 2, 3]),
"bar": Data([1, 2, 3])
]

assertDecoderSucceeds(decoding: value, from: dictionary)
}

func testThatDecoderFailsWhenDecodingInvalidDataToBlob() {
decoder.dataDecodingStrategy = .blob

let dictionary = ["foobar": "AQID"]

assertDecoderFails(decoding: [String: Data].self, from: dictionary) { error in
switch error {
case let DecodingError.typeMismatch(type, _) where type is Data.Type:
return true

default:
return false
}
}
}

func testThatDecoderSucceedsWhenDecodingDataUsingCustomFunction() {
decoder.dataDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
Expand Down
10 changes: 8 additions & 2 deletions Tests/Decoder/DictionaryDecoderTesting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ extension DictionaryDecoderTesting {

jsonDecoder.keyDecodingStrategy = decoder.keyDecodingStrategy.jsonDecodingStrategy
jsonDecoder.dateDecodingStrategy = decoder.dateDecodingStrategy.jsonDecodingStrategy
jsonDecoder.dataDecodingStrategy = decoder.dataDecodingStrategy.jsonDecodingStrategy
jsonDecoder.dataDecodingStrategy = try XCTUnwrap(
decoder.dataDecodingStrategy.jsonDecodingStrategy,
"JSON has no data type, pass the expected value instead."
)
jsonDecoder.nonConformingFloatDecodingStrategy = decoder.nonConformingFloatDecodingStrategy.jsonDecodingStrategy

let data = try JSONSerialization.data(withJSONObject: dictionary, options: .fragmentsAllowed)
Expand Down Expand Up @@ -169,14 +172,17 @@ extension DictionaryDataDecodingStrategy {

// MARK: - Instance Properties

fileprivate var jsonDecodingStrategy: JSONDecoder.DataDecodingStrategy {
fileprivate var jsonDecodingStrategy: JSONDecoder.DataDecodingStrategy? {
switch self {
case .deferredToData:
return .deferredToData

case .base64:
return .base64

case .blob:
return nil

case let .custom(closure):
return .custom(closure)
}
Expand Down
8 changes: 8 additions & 0 deletions Tests/Encoder/DictionaryEncoderStrategiesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ final class DictionaryEncoderStrategiesTests: XCTestCase, DictionaryEncoderTesti
assertEncoderSucceeds(encoding: value)
}

func testThatEncoderSucceedsWhenEncodingDataToBlob() {
encoder.dataEncodingStrategy = .blob

let value = ["foobar": Data([1, 2, 3])]

assertEncoderSucceeds(encoding: value, expecting: value)
}

func testThatEncoderSucceedsWhenEncodingDataUsingCustomFunction() {
encoder.dataEncodingStrategy = .custom { data, encoder in
var container = encoder.singleValueContainer()
Expand Down
10 changes: 8 additions & 2 deletions Tests/Encoder/DictionaryEncoderTesting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ extension DictionaryEncoderTesting {

jsonEncoder.keyEncodingStrategy = encoder.keyEncodingStrategy.jsonEncodingStrategy
jsonEncoder.dateEncodingStrategy = encoder.dateEncodingStrategy.jsonEncodingStrategy
jsonEncoder.dataEncodingStrategy = encoder.dataEncodingStrategy.jsonEncodingStrategy
jsonEncoder.dataEncodingStrategy = try XCTUnwrap(
encoder.dataEncodingStrategy.jsonEncodingStrategy,
"JSON has no data type, pass the expected dictionary instead."
)
jsonEncoder.nonConformingFloatEncodingStrategy = encoder.nonConformingFloatEncodingStrategy.jsonEncodingStrategy

let data = try jsonEncoder.encode(value)
Expand Down Expand Up @@ -135,14 +138,17 @@ extension DictionaryDataEncodingStrategy {

// MARK: - Instance Properties

fileprivate var jsonEncodingStrategy: JSONEncoder.DataEncodingStrategy {
fileprivate var jsonEncodingStrategy: JSONEncoder.DataEncodingStrategy? {
switch self {
case .deferredToData:
return .deferredToData

case .base64:
return .base64

case .blob:
return nil

case let .custom(closure):
return .custom(closure)
}
Expand Down
Loading