Giter Club home page Giter Club logo

Comments (11)

soumyamahunt avatar soumyamahunt commented on August 15, 2024 2

Sample app is attached, please don't return non-optional Date from the decode(from:) method as a workaround solution to the problem, because I need to store nil Date? in the model for some cases.

As I mentioned, for storing Date? you need to return Date in decode(from:) and return Date? in decodeIfPresent(from:). Your DateCoder implementation should look like this:

struct DateCoder: HelperCoder {
     func decode(from decoder: Decoder) throws -> Date {
         // return Date, this implementation will be used when you are storing Non-optional Date in Model
     }

    func decodeIfPresent(from decoder: Decoder) throws -> Date? {
         // return optional Date, this implementation will be used when you are storing optional Date in Model
     }
 }

Please try to follow this guideline and try to write some test cases for your use cases, please let me know if any test case fails with your implementation code attached.

from metacodable.

soumyamahunt avatar soumyamahunt commented on August 15, 2024

I have the following @Codable model and its HelperCoder struct:

@Codable
struct TestModel {
    
    @CodedBy(DateCoder())
    var startDate: Date?
}

struct DateCoder: HelperCoder {
    func decode(from decoder: Decoder) throws -> Date? {
        var container = try decoder.unkeyedContainer()
        let dateStr = try container.decode(String.self)
        let df = ISO8601DateFormatter()
        let date = df.date(from: dateStr)
        return date
    }
}

Can you try with returning Date type instead of Date? in DateCoder implementation? That should fix your issue:

 struct DateCoder: HelperCoder {
     func decode(from decoder: Decoder) throws -> Date {
         var container = try decoder.unkeyedContainer()
         let dateStr = try container.decode(String.self)
         let df = ISO8601DateFormatter()
         let date = df.date(from: dateStr)
         return date
     }
 }

from metacodable.

murad1981 avatar murad1981 commented on August 15, 2024

That solves the syntax error but doesn't solve the problem, there should be a way to decode optional Dates by returning optional Date from the HelperCoder.

There are lots of use cases where we need something like: var someDate: Date? in the models .

If I implement decodeIfPresent(from: ) instead of decode(from:) in the HelperCoder, the compiler auto completes the function signature with the return type of Date?? which should be Date?.

I also tried to use @Default(nil) for the optional date in the model but it also gives an error: Generic parameter 'T' could not be inferred.

from metacodable.

soumyamahunt avatar soumyamahunt commented on August 15, 2024

That solves the syntax error but doesn't solve the problem, there should be a way to decode optional Dates by returning optional Date from the HelperCoder.

This use-case can be handled with current implementation as well, HelperCoder has optional requirement decodeIfPresent(from:), which you can implement in your DateCoder, to indicate what happens when you want to decode optional Date.

The default implementation of decodeIfPresent(from:) just returns result from decode(from:) and returns nil if this method throws error.

from metacodable.

murad1981 avatar murad1981 commented on August 15, 2024

That solves the syntax error but doesn't solve the problem, there should be a way to decode optional Dates by returning optional Date from the HelperCoder.

This use-case can be handled with current implementation as well, HelperCoder has optional requirement decodeIfPresent(from:), which you can implement in your DateCoder, to indicate what happens when you want to decode optional Date.

The default implementation of decodeIfPresent(from:) just returns result from decode(from:) and returns nil if this method throws error.

kindly look at the edited comment above regarding decodeIfPresent(from:) which produces the same problem as well.

from metacodable.

soumyamahunt avatar soumyamahunt commented on August 15, 2024

If I implement decodeIfPresent(from: ) instead of decode(from:) in the HelperCoder, the compiler auto completes the function signature with the return type of Date?? which should be Date?.

Can you share the implementation snippet you are trying? I will be able to suggest changes that will make your use-case work.

from metacodable.

murad1981 avatar murad1981 commented on August 15, 2024

Sample app is attached, please don't return non-optional Date from the decode(from:) method as a workaround solution to the problem, because I need to store nil Date? in the model for some cases.
TestCodableMacro.zip

from metacodable.

murad1981 avatar murad1981 commented on August 15, 2024

Thank you @soumyamahunt for the last pattern you provided which really solves the issue, but the only take away in this pattern is that I must provide the implementation of decode(from:) to avoid the compiler error: Type 'DateCoder' does not conform to protocol 'HelperCoder', so I just implemented it like this to return a dummy date:

func decode(from decoder: Decoder) -> Date {
         Date()
    }

if you could fix this to only require func decodeIfPresent(from:) when the model value is optional that would be cool

from metacodable.

soumyamahunt avatar soumyamahunt commented on August 15, 2024

if you could fix this to only require func decodeIfPresent(from:) when the model value is optional that would be cool

The use of HelperCodable is to provide custom decoder/encoder that will decode/encode a type and it's optional variant. This is the same in principle with the standard library where decode is used to decode a basic type (i.e. Int) decodeIfPresent to decode optional of that type.

You should implement decode(from:) to decode the actual Date type and reuse that implementation in decodeIfPresent(from:) and provide custom logic to handle failure (i.e. returning nil).

Anyway, closing this since there is no issue here.

from metacodable.

murad1981 avatar murad1981 commented on August 15, 2024

Posting this comment after closing the issue ..

Sorry but that sounds unreasonable to provide implementations for both the basic type and its optional variant for the case when the value in question may or may not return, providing only: decodeIfPresent implementation is enough, like we already do with the init(from decoder:).

Moreover, the implementation of the basic type and its optional variant will not be the same, so it can't be reused in both.

I'm stuck in a similar case, to do a very basic data conversion (string to int), providing a whole struct with double implementations produces too much boilerplate code. (i've emailed. you about it by the way).

@soumyamahunt

from metacodable.

rursache avatar rursache commented on August 15, 2024

I had the same issue as @murad1981 and managed to fix it.
While the last messages here went a bit off the rail, the fix is really easy:

@Codable
struct Demo {
    @CodedBy(CustomDateCoder(format: "yyyy-MM-dd"))
    var myDate: Date

    @CodedBy(CustomDateCoder(format: "HH:mm"))
    var myTime: Date?
}
fileprivate struct CustomDateCoder: HelperCoder {
    var format: String
    
    func decode(from decoder: any Decoder) throws -> Date {
        let container = try decoder.singleValueContainer()
        let dateString = try container.decode(String.self)
        if dateString.isEmpty {
            throw DecodingError.valueNotFound(Date.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Date string is empty"))
        }
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = format
        guard let date = dateFormatter.date(from: dateString) else {
            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Cannot format date"))
        }
        return date
    }
}

from metacodable.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.