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

Dealing with optional variables (basic types and enum) #81

Open
jimmyti opened this issue Nov 12, 2016 · 3 comments
Open

Dealing with optional variables (basic types and enum) #81

jimmyti opened this issue Nov 12, 2016 · 3 comments

Comments

@jimmyti
Copy link

jimmyti commented Nov 12, 2016

I am learning how to use Genome, and so far I am impressed with the flexibility offered by Genome. 👍

  1. Is it possible to declare optional variables (both basic type and enum) that will not be mapped to JSON at all if they are nil?
  2. An extension to this question is how do I specify the transformToNode and transformFromNode functions for these optional variables?
  3. I am trying to extend Date class, making it NodeRepresentable and NodeConvertible. (See code below). Would this implementation be able to handle when date is nil during serialization (my intention is to skip saving date data if date is nil) and when the JSON data is absent during deserialization (my intention is to leave the date variable uninitialized)?
extension Date: NodeRepresentable {
    public func makeNode(context: Context = EmptyNode) throws -> Node {
        return .string(self.ISO8601String())
    }
}

extension Date: NodeConvertible {
    public init(node: Node, in context: Context) throws {
        guard let string = node.string else {
            throw NodeError.unableToConvert(node: node, expected: "\(String.self)")
        }
        guard let date = Date.dateFromISO8601String(string: string) else {
            throw NodeError.unableToConvert(node: node, expected: "Date is stored in ISO8601 format")
        }
        self = date
    }
}

Example class definition:

enum FooType: Int8 {
    case typeA
    case typeB
}

class Bar: BasicMappable {
    var foo: FooType?
    var date: Date?
    var name: String?
    
    required init() {
    }
    
    func sequence(map: Map) throws {
        try foo <~> map["foo"]
        try date <~> map["date"]
        try name <~> map["name"]
    }
}
@loganwright
Copy link
Owner

@jimmyti there's a lot here, so let me know if I miss something.

  1. What you have should automatically return nil for date property if the json at date is nil. If that's not what you're seeing, there may be a bug. The expected behavior would be that if something is optional and nil is received in JSON, it should become nil
  2. You can define individually by simply adding .transform chained to the end
try date <~> map["date"]
    .transformFromNode(stringToOptionalDate)
    .transformToNode(optionalDateToString)
  1. It should be able to handle it. Internally Genome checks if JSON key is nil before passing to initializers. If it is nil and user is expecting optional we return nil, otherwise return error.

Let me know if you see other things!

@jimmyti
Copy link
Author

jimmyti commented Nov 14, 2016

@loganwright thank you for answering!

  1. In the case of transforming from variable to JSON, can I assume that the input to the transformToNode func will always be non-nil? transformToNode doesn't seem to like me returning an optional there. In other words, is it safe to code transformToNode for foo as such (using forced unwrapped):
  2. I have included a variable foos with type of [FooType]. Would you be able to show me the code to transform [FooType], i.e. transformToNode, transformFromNode.
enum FooType: Int8 {
    case typeA
    case typeB
}

class Bar: BasicMappable {
    var foo: FooType?
    var date: Date?
    var name: String?
    var foos: [FooType]?

    required init() {
    }

    func sequence(map: Map) throws {
        try foo <~> map["foo"]
            .transformFromNode {
                FooType(rawValue: $0)
            }
            .transformToNode {
                $0!.rawValue
            }
        try date <~> map["date"]
        try foos <~> map["foos"]
            .transformFromNode {
                // how to handle transforming node to enum collection
            }
            .transformToNode {
                // how to handle transforming enum collection to JSON
            }
    }
}

@loganwright
Copy link
Owner

@jimmyti let me look into the optional thing, you should be able to, but the transform functions are super generic, and sometimes require more type information in unexpected ways. For this stuff though, I think you'd reduce a lot of your code by conforming FooType to NodeConvertible. Once you do that, you shouldn't need transformers for foo or foos, it'd just be:

    func sequence(map: Map) throws {
        try foo <~> map["foo"]
        try date <~> map["date"]
        try foos <~> map["foos"]
    }

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

No branches or pull requests

2 participants