Comments (3)
Here's an example of how to implement the custom reflection methods for desired effect.
struct User: Reflectable, Codable {
var email: String
var active: Bool
static func reflectProperty<T>(forKey keyPath: KeyPath<User, T>) throws -> ReflectedProperty? {
let name: String
switch keyPath {
case \User.email: name = "email"
case \User.active: name = "active"
default: return nil
}
return .init(T.self, at: [name])
}
init(from decoder: Decoder)throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.email = try container.decode(String.self, forKey: .email)
self.active = try container.decodeIfPresent(Bool.self, forKey: .active) ?? false
}
}
try XCTAssertEqual(User.reflectProperties().description, "[email: String, active: Bool]")
try XCTAssertEqual(User.reflectProperty(forKey: \.email)?.description ?? "n/a", "email: String")
try XCTAssertEqual(User.reflectProperty(forKey: \.active)?.description ?? "n/a", "active: Bool")
from core.
After digging into this a bit more, the issue seems unavoidable.
The problem is that false
happens to be the value that the reflection decoder sets when a Bool property is active. (Bool has two cases which is the minimum to represent a "hi" and "lo" value. This is how the reflection decoder determines a hit or miss. Ignore the fact that false
is a somewhat unintuitive "hi" value--they just need to be distinct).
The problem is that false
is being set as a default value in the init(from:)
method in question. When active
is set to false
, the reflection decoder gets a false positive that it has correctly identified the coding path for the property even though it hasn't. You can easily prove this by setting the default value to true
instead of false
. Reflection works normally.
TL;DR is that the algorithm used to power Codable reflection is not compatible with setting default values in a custom init(from:)
decoder method. At least where those default values are the reflection decoder's "hi" value--which should be considered opaque.
Fortunately Codable reflection is protocol based and you can work around this issue by simply implementing the reflectProperty
method manually. See https://api.vapor.codes/core/latest/Core/Protocols/Reflectable.html#/s:4Core11ReflectableP15reflectProperty6forKeyAA09ReflectedD0VSgs0F4PathCyxqd__G_tKlFZ.
Until Swift gets better reflection, this is really our best option. The alternative to using Codable-based reflection is having every model implement reflectProperty
and reflectProperties
(something the very early Fluent 3 alphas required) or using Strings for for query filtering. Both of those options suck worse...for lack of a better word.
Also worth noting that the init(from:)
method can be done without the annoying do / catch
by using contains
. Although this still doesn't reflect the correct property.
init(from decoder: Decoder)throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.email = try container.decode(String.self, forKey: .email)
if container.contains(.active) {
self.active = try container.decode(Bool.self, forKey: .active)
} else {
self.active = false
}
}
from core.
Thanks @tanner0101 !
The custom implementation of reflectProperty seems to be the most appropriate way to deal with it, though I like the contain
approach as well.
I think I'll go with the contain
approach for now, that seems most natural.
from core.
Related Issues (20)
- Data does not conform to ReflectionDecodable HOT 1
- Possible bug in Async.flatten HOT 1
- [Feature] Change Decodable.decodeProperty(forKey:) Parameter Type to AnyKeyPath
- LazyFuture should support throwing
- "Free" ReflectionDecodable conformance for CaseIterable enums HOT 1
- Replace `Collection<Future>.flatten` with `Future.reduce`? HOT 8
- Process.execute on macOS leaves a background thread spinning at 100% HOT 2
- Does Core support Cocoapods? HOT 9
- Is `Future.map(on: worker)` intentionally synchronous? HOT 6
- Process.asyncExecute has threading issue
- Replace `Future.flatMap` with `Future.then`? HOT 2
- Set should conform to ReflectionDecodable HOT 1
- core doesn't compile on tvOS and WatchOS HOT 2
- NSError conformance to Debuggable breaks type casting HOT 4
- ReflectionKeyedDecoder.decode() is not returning the "truthy" value when the context is active
- 2 Warnings during linux build with 5.0.x swift HOT 4
- Reflection very slow and using a lot of memory HOT 2
- DirectoryConfig.workDir not working with Xcode 11 integration with SPM HOT 8
- threading issues
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from core.