Skip to content
Open
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
9 changes: 2 additions & 7 deletions Sources/FoundationEssentials/CodableWithConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ public extension KeyedEncodingContainer {
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
public extension KeyedDecodingContainer {
func decode<T, C>(_: CodableConfiguration<T?, C>.Type, forKey key: Self.Key) throws -> CodableConfiguration<T?, C> {
if self.contains(key) {
let wrapper = try self.decode(CodableConfiguration<T, C>.self, forKey: key)
return CodableConfiguration<T?, C>(wrappedValue: wrapper.wrappedValue)
} else {
return CodableConfiguration<T?, C>(wrappedValue: nil)
}
let wrapper = try self.decodeIfPresent(CodableConfiguration<T?,C>.self, forKey: key)
return CodableConfiguration<T?, C>(wrappedValue: wrapper?.wrappedValue)
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ extension AttributeScopes.TestAttributes {
return NonCodableType(inner: inner)
}
}

struct NonCodableType : Hashable {
var inner : Int
}
}

#if FOUNDATION_FRAMEWORK
Expand All @@ -121,10 +125,6 @@ extension AttributeScopes.TestAttributes.TestBoolAttribute : MarkdownDecodableAt
extension AttributeScopes.TestAttributes.TestDoubleAttribute : MarkdownDecodableAttributedStringKey {}
#endif // FOUNDATION_FRAMEWORK

struct NonCodableType : Hashable {
var inner : Int
}

extension AttributeScopes {
var test: TestAttributes.Type { TestAttributes.self }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Testing

#if canImport(FoundationEssentials)
@testable import FoundationEssentials
#else
@testable import Foundation
#endif

@Suite("CodableConfiguration")
struct CodableConfigurationTests {

@Test
func decodingIndirectly_succeedsForNull() async throws {
let json = "{\"testObject\":null}"
let jsonData = try #require(json.data(using: .utf8))

let decoder = JSONDecoder()
let sut = try decoder.decode(UsingCodableConfiguration.self, from: jsonData)
#expect(sut.testObject == nil)
}

@Test
func decodingIndirectly_succeedsForCorrectDate() async throws {
let json = "{\"testObject\":\"Hello There\"}"
let jsonData = try #require(json.data(using: .utf8))

let decoder = JSONDecoder()
let sut = try decoder.decode(UsingCodableConfiguration.self, from: jsonData)
#expect(sut.testObject?.value == "Hello There")
}

@Test
func decodingIndirectly_succeedsForMissingKey() async throws {
let json = "{}"
let jsonData = try #require(json.data(using: .utf8))

let decoder = JSONDecoder()
let sut = try decoder.decode(UsingCodableConfiguration.self, from: jsonData)
#expect(sut.testObject == nil)
}
}

private extension CodableConfigurationTests {
struct NonCodableType {
let value: String
}

/// Type that uses CodableConfiguration for decoding Optional NonCodableType
struct UsingCodableConfiguration: Codable {
@CodableConfiguration(wrappedValue: nil, from: CustomConfig.self)
var testObject: NonCodableType?
}

/// Helper object allowing to decode Date using ISO8601 scheme
struct CustomConfig: DecodingConfigurationProviding, EncodingConfigurationProviding, Sendable {
static let encodingConfiguration = CustomConfig()
static let decodingConfiguration = CustomConfig()

func decode(from decoder: any Decoder) throws -> NonCodableType {
let container = try decoder.singleValueContainer()
let value = try container.decode(String.self)
return NonCodableType(value: value)
}

func encode(object: NonCodableType, to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(object.value)
}
}
}

extension CodableConfigurationTests.NonCodableType: CodableWithConfiguration {
typealias CustomConfig = CodableConfigurationTests.CustomConfig

public init(from decoder: any Decoder, configuration: CustomConfig) throws {
self = try configuration.decode(from: decoder)
}

public func encode(to encoder: any Encoder, configuration: CustomConfig) throws {
try configuration.encode(object: self, to: encoder)
}
}