In this package which uses property wrappers to affect the how types are decoded, Resilient is a struct which is Decodable but is never actually intended to be initialized via init(from:) (indeed, that codepath currenly asserts). I would be able to remove this assertion, conformance and initializer if the synthesized Decodable initializer didn't require that the type of the property be Decodable, only that the synthesized call to container.decode(Foo.self, forKey: .foo) type checked correctly. We would then be able to catch these asserts at compile-time, and not need to provide a fallback path.
Would you not want to prevent using this property wrapper with types which "should not" be used with it?
I.e. rather than Value: Codable it seems you'd want to offer it for more specific types, where Value: Optional where Wrapped: Codablespecifically? If I understand what you're after here: you want to allow "fallback to nil if fails" right? So how about pushing the failure from runtime to compile time, when you know the construction is wrong?
The correct method is selected from the overloads of decode defined on KeyedDecodingContainer. This uses the basic Swift overload-resolution mechanisms, so if I were to define a decode(MyNotDecodableType.self, forKey: Key) on KeyedDecodingContainer, it would happily call that method whether or not MyNotDecodableType conformed to Decodable or not. CodableWrappers uses a similar approach.
Ok, I looked at the package. I'm surprise that it works. Of course the synthesiser uses the concrete type and so can figure out the right "overload".
Anyhow, I suppose it could be technically possible (discarding the compatibility issues) to change the checking of T: Codable to KeyedValueContainer.decode(T.self, ...).
I agree with @ktoso that you're allowing more conformance much more than it should, that Resilient<Int> is Codable when it shouldn't. Though I couldn't think of a way to easily express it in the current type system.
Further, I'd expect to be able to decode [Resilient<T>], and top-level Resilient<T>, successfully, which would still go through Resilient.init(from:).
Yeah, the reason that I explicitly assert when decoding things like [Resilient<T>], is that the behavior is no different from just decoding [T] so it is very likely the developer made a mistake. This is the kind of semantics we would be able to model if the synthesized init(from:) tried to type check the synthesized function calls rather than just verifying that the property was Decodable.
Recent development snapshot toolchains have actually added a warning for this situation:
❱ TOOLCHAINS=org.swift.50202004131a swiftc -c - <<'EOF'
struct Foo: Decodable {
let y: Int
let z: Int = 42
}
EOF
<stdin>:3:7: warning: immutable property will not be decoded because it is declared with an initial value which cannot be overwritten
let z: Int = 42
^
<stdin>:3:7: note: set the initial value via the initializer or explicitly define a CodingKeys enum without a 'z' case to silence this warning
let z: Int = 42
^
<stdin>:3:7: note: make the property mutable instead
let z: Int = 42
~~~ ^
var