I'm struggling to understand how best to represent dates and ensure they can be encoded and decoded consistently between Service and Client when both the service and client and written in Swift.
For context, my server is written using Vapor, Fluent, Graphiti, and GraphQLSwift. My client is using Apollo for iOS.
It's my understanding that I need to create a custom scalar type when defining the schema which I've done using Scalar(Date.self) in my schema definition.
On my server side my models have date fields like @Field(key: "date_purchased") var datePurchased: Date?. The data is stored in the database (Postgres) as a timestamp with timezone field.
When I test the server in a GraphQL playground my date field looks like "datePurchased": 1704407422, which appears to be a double value that seems to be a UNIX timestamp.
On the client side (Apollo) I create a custom schema type:
public extension ClientAPI {
typealias Date = String
}
extension Foundation.Date: CustomScalarType {
public init(_jsonValue value: JSONValue) throws {
guard let dateString = value as? String else {
throw JSONDecodingError.couldNotConvert(value: value, to: String.self)
}
guard let date = ISO8601DateFormatter().date(from: dateString) else {
throw JSONDecodingError.couldNotConvert(value: value, to: Date.self)
}
self = date
}
public var _jsonValue: JSONValue {
self.timeIntervalSince1970
}
}
Also, I noticed if I used the fluent @Timestamp property wrapper @Timestamp(key: "created_at", on: .create) var dateCreated: Date? and view this in the playground it pulls through as "dateCreated": 1705619227.214435 with decimal places?
What is the best way to reliably manage date formats between client and server when both are using Swift's Foundation.Date? Is it possible to use other date formats in the external API other than UNIX timestamps?
I'm not familiar with your server-side technologies, but am quite familiar with Swift and Apple's Foundation framework, where
Dateis defined.Apple's
Dateobjects have an "epoch" (zero) date of midnight on January 1, 2001. UNIX dates use an epoch date of January 1, 1970. I converted your example date of 1704407422 to a Date usingprint(Date(timeIntervalSince1970: 1704407422))and got an output of2024-01-04 22:30:22 +0000. That suggests to me that your dates are UNIX dates.On the iOS side, you should be able to convert your numeric dates to
Dateobjects using the initializerinit(timeIntervalSince1970: TimeInterval)like in the example above. I don't know if that initializer is available in your Server-side Swift, but assume that it would be.##Edit:
Note that on iOS/macOS, Dates record a decimal number of seconds since the epoch date. That lets you measure fractions of a second. (Internally they use a Double.) You can simply truncate the value to an integer if you don't want fractional seconds. (I believe the Unix standard is to use an Int64 to represent a date value.)