aryaxt
aryaxt

Reputation: 77596

Decoding Timestamp returned from a firebase function

I have a firebase function that returns an object including a timestamp

[{
    startTime: firestore.Timestamp.fromDate(randomDate),
    duration: 45 
}]

In swift I have a model representing this data

struct AvailabilityTimeSlot: Codable {
    let startTime: Date
    let int: Duration
}

Followed by the code calling the function and decoding it

func fetchTimeSlotAvailability(date: Date, groomerId: String,  requestedServices: [Pet: [PetServiceAndPricing]]) async throws -> [AvailabilityTimeSlot] {
    let result = try await Functions.functions().httpsCallable("availableBookingTimeSlots")
        .call([
        "date": date.timeIntervalSince1970,
        "groomerId": groomerId,
        "requestedServices": requestedServices
    ])
    
    return try Firestore.Decoder().decode([AvailabilityTimeSlot].self, from: result.data)
}

The returning data looks like

[{
duration = 45;
startTime =         {
    "_nanoseconds" = 341000000;
    "_seconds" = 1673116451;
};]

But I'm getting decoding error

keyNotFound(TimestampKeys(stringValue: "seconds", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "startTime", intValue: nil)], debugDescription: "No value associated with key TimestampKeys(stringValue: \"seconds\", intValue: nil) (\"seconds\").", underlyingError: nil))

What's the recommended way of receiving dates from firebase functions? Timestamps have been working as expected when using FireStore for reading data, but not for functions

Upvotes: 0

Views: 331

Answers (2)

Maksym
Maksym

Reputation: 1

That works for me in Swift

struct YourName: Codable {
var date: Timestamp?
... anotherValues

enum CodingKeys: String, CodingKey {
    case date
    ... anotherValues
}

enum TimestampKeys: String, CodingKey {
     case seconds
     case nanoseconds
     case _seconds
     case _nanoseconds
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    
    if let timestampContainer = try? container.nestedContainer(keyedBy: TimestampKeys.self, forKey: .date),
       let seconds = (try? timestampContainer.decodeIfPresent(Int.self, forKey: .seconds)) ?? (try? timestampContainer.decodeIfPresent(Int.self, forKey: ._seconds)),
       let nanoseconds = (try? timestampContainer.decodeIfPresent(Int.self, forKey: .nanoseconds)) ?? (try? timestampContainer.decodeIfPresent(Int.self, forKey: ._nanoseconds)) {
        date = Timestamp(seconds: Int64(seconds), nanoseconds: Int32(nanoseconds))
    } else {
        date = try container.decodeIfPresent(Timestamp.self, forKey: .date)
    }

    device = try container.decodeIfPresent(String.self, forKey: .device)
}
}

Upvotes: 0

Doug Stevenson
Doug Stevenson

Reputation: 317427

Since callable type functions always return JSON that's serialized automatically from the data you provide, you might want to make a decision about how you want to serialize the timestamp's data in a way that's compatible with JSON. By default, the function is just going to dump the internal representation of any found object to the client, as you can see:

{
    "_nanoseconds" = 341000000;
    "_seconds" = 1673116451;
}

Those are the internal fields of the Timestamp object. Note that the keys are prefixed with an underscore - this is part of the timestamp's implementation details that you are getting by default.

If you want your frontend code to reach into those dumped timestamp implementation details, that's up to you to decide. You could even reconstitute a new Timestamp in your app using those values. Or you can be intentional about writing code to send exactly the values you want (which will be converted to JSON) from the timestamp you have in the function, and make sure your app code is prepared to receive that data and convert it to whatever you need.

See also:

Upvotes: 1

Related Questions