Skip to content

Enable handling of types that require validation#13

Merged
djones6 merged 9 commits intomasterfrom
validation
Oct 10, 2018
Merged

Enable handling of types that require validation#13
djones6 merged 9 commits intomasterfrom
validation

Conversation

@djones6
Copy link
Copy Markdown
Contributor

@djones6 djones6 commented Oct 3, 2018

Description

This PR makes the DummyCodingValueProvider and DummyKeyedCodingValueProvider protocols public, for use with user types that validate field values during the init(from: Decoder) initializer.

The types have been renamed to ValidSingleCodingValueProvider and ValidKeyedCodingValueProvider, respectively.

Motivation and Context

As part of the process of 'discovering' the type information for a Swift type, TypeDecoder creates an instance of the type by supplying dummy values for fields.

This works fine for types that do not validate the specific values of those fields, however there are cases where this scheme does not work - for example with Foundation types such as URL, where instance creation will fail if the string value provided does not have valid syntax.

To cater for those specific Foundation types, TypeDecoder has a pair of protocols: DummyCodingValueProvider and DummyKeyedCodingValueProvider, that allow an extension of a type to declare conformant ('dummy') values while simulating decoding. However, these are currently not public and so user types are unable to use this method.

We considered requiring users to break the decoding and validation process into two parts: leaving the init(from: Decoder) to mechanically decode the values, and then have a separate Validatable protocol which can be conformed to, which would house the validation logic. Kitura would then requie a validate() function to succeed before passing a Codable type through to the user's route handler.

However, this is insufficient for two reasons:

  1. The author of the Kitura application may not own the type: if may be shared with other projects, or part of another public API, and so not possible to change the decoding logic,
  2. If a Codable enum is declared with a String raw value (eg: enum Colour: String, Codable), the compiler will automatically synthesize an init(from: Decoder) implementation that performs validation on the values being passed in (to ensure they match one of the declared cases).

The solution proposed here is to require such validating types to be extended with a conformance to the appropriate protocol above, in order to supply valid dummy values.

Copy link
Copy Markdown
Contributor

@ianpartridge ianpartridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have an example showing how to make types with failable initializers decodable. e.g.:

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

@djones6
Copy link
Copy Markdown
Contributor Author

djones6 commented Oct 5, 2018

@ianpartridge I believe that example works with Codable already (as the init(from: Decoder) operates independent of other initializers) and the TypeDecoder can work with it as-is. The issues arise when init(from: Decoder) chooses to throw.

Copy link
Copy Markdown
Contributor

@ianpartridge ianpartridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I think we just need a few more comments in the examples. Also, should we call this Valid instead of Dummy?

@djones6
Copy link
Copy Markdown
Contributor Author

djones6 commented Oct 5, 2018

Yes, the Decoder could give you a hint that the type may require conformance to those protocols (and we'd be able to give the exact name ie. Keyed or Single as we know the context).

My intention was to also keep track of the most recent key that we were asked to decode, and the dummy value we provided - however, it occurred to me that this won't work in general, as the implementation of init(from: Decoder) could choose to read all the fields, then perform validation, so it's not necessarily the most recent field that was accessed that caused a validation error. Maybe we have to stick with whatever message the initializer chooses to throw.

@djones6 djones6 changed the title [WIP] Enable handling of types that require validation Enable handling of types that require validation Oct 5, 2018
@ianpartridge ianpartridge removed the request for review from seabaylea October 8, 2018 15:22
@djones6 djones6 added the jazzy-doc When applied to a PR, instructs Package-Builder to regenerate the Jazzy documentation label Oct 8, 2018
@djones6
Copy link
Copy Markdown
Contributor Author

djones6 commented Oct 8, 2018

@ianpartridge Docs look fine locally, so this is ready to be merged once Travis completes

@ianpartridge
Copy link
Copy Markdown
Contributor

This looks excellent, thank you.

@djones6 djones6 merged commit 2fd9b41 into master Oct 10, 2018
@djones6 djones6 deleted the validation branch October 10, 2018 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jazzy-doc When applied to a PR, instructs Package-Builder to regenerate the Jazzy documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants