Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add/Retrieve Codable objects in a session #54

Merged
merged 10 commits into from
Dec 13, 2018
Merged

Conversation

Andrew-Lees11
Copy link
Contributor

This PR adds two function to add or retrieve codable objects from a session as a fix for issue #53.

The two function are:

public func add<T: Encodable>(_ value: T, forKey key: String) throws
public func read<T: Decodable>(as type: T.Type, forKey key: String) throws -> T { 

Tests, Jazzy docs and the README have been updated to cover the new API.

case keyNotFound(key: String)

//Thrown when a primative Decodable or array of primative Decodables fails to be cast to the provided type.
case failedPrimativeCast()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Primative -> Primitive (throughout this PR)

Copy link
Collaborator

Choose a reason for hiding this comment

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

() is unnecessary.

// MARK StoreError

/// An error indicating the failure of an operation to encode/decode into/from the session `Store`.
public enum SessionCodingError: Swift.Error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please make this a struct instead of an enum so we can add to it without doing a major release.

public func add<T: Encodable>(_ value: T, forKey key: String) throws {
let json: Any
if isPrimative(value: value) {
json = value
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need this special casing for primitives?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JSONEncoder and JSONDecoder do not have an .allowFragments option and will throw an error if you try and encode/decode a primitive type using them. This has been raised as a bug in SR-6163.
Until that is implemented, isPrimitive handles the case where someone wants to store/retrieve a primitive type or array of primitives type

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK - I think we should consider not allowing primitives and throwing if the serialization fails. What do you think?

@Andrew-Lees11
Copy link
Contributor Author

We have changed this to use subscript with Codable instead of now functions. the API from aboe would now be:

session["key"] = codable
let retrieved: Type? = session["key"]

@Andrew-Lees11 Andrew-Lees11 added this to the 2018.25 milestone Dec 11, 2018
}
let user: User? = request.session?[userID]
response.status(.OK)
response.send(user)
Copy link
Contributor

Choose a reason for hiding this comment

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

Does response.send with an optional Codable do what you expect?

guard let dict = state[key] else {
return nil
}
if let primative = dict as? T {
Copy link
Contributor

Choose a reason for hiding this comment

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

primitive

/// - Parameter key: The Codable key of the entry to retrieve/save.
public subscript<T: Codable>(key: String) -> T? {
get {
guard let dict = state[key] else {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really a dictionary or should this be named something like value?

Copy link
Contributor

@djones6 djones6 left a comment

Choose a reason for hiding this comment

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

This process works, but is very inefficient:

session["key"] = value calls JSONEncoder.encode(value), which creates a dictionary representation of the value, then calls JSONSerialization.data() to return you a Data.
Then we take that data and call JSONSerialization.jsonObject() to get back a dictionary. Store that in the session.
When it's time to save the session, we invoke JSONSerialization.data() on the whole session store to produce the JSON data that we will persist.

This bouncing from type -> dictionary -> data -> dictionary -> data is expensive. Ideally we'd either do:

  • type -> dictionary -> data (JSONEncoder without the final call to JSONSerialization)
  • type -> data -> data (if we could have JSONSerialization nest the ready-prepared Data representation inside the data it is building)

The same applies to the decode.

However, fixing this wouldn't require a public API change, so could be done later.

@djones6 djones6 merged commit 7d2d50e into master Dec 13, 2018
@djones6 djones6 deleted the saveCodable branch December 13, 2018 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants