SwiftGraphQL by default encodes all fields that reference unconfigured scalars as AnyCodable values. For easier decoding and better type support you can, however, create custom GraphQLScalar structures.

To create a custom scalar, you need to create a structure that conforms to the GraphQLScalar protocol.

/// Protocol that a custom scalar should implement to be used with SwiftGraphQL.
public protocol GraphQLScalar: Encodable {

    /// A decoder from the any-type codable value.
    init(from: AnyCodable) throws

    /// Default value that mocks the returned value.
    static var mockValue: Self { get }
}

It doesn’t matter where you implement the conformance as long as it’s in the same project as the generated code.

Because GraphQLScalar uses the Encodable protocol to encode the value, you might need to customize how JSONEncoder encodes the built-in value in case you are mapping a custom scalar to a built-in type. Depending on your target, you should either

  • pass a custom JSONEncoder as a paramater to the URLRequest.query method,
  • provide a custom encoder to FetchExchange
  • modify the encoder in GraphQLWebSocketConfiguration.
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601

// Vanilla Request
let request = URLRequest(url: API_ENDPOINT)
request.query(selection, encoder: encoder)

// FetchExchange
let client = SwiftGraphQLClient.Client(
    request: API_ENDPOINT,
    exchanges: [
        FetchExchange(encoder: encoder)
    ]
)

// GraphQLWebSocketConfiguration
let config = GraphQLWebSocketConfiguration()
config.encoder = encoder

let ws = GraphQLWebSocket(request: request, config: config)

Example DateTime Scalar

import Foundation
import GraphQL
import SwiftGraphQL

extension Date: GraphQLScalar {
    public init(from codable: AnyCodable) throws {
        guard let raw = codable.value as? String else {
            throw DateTimeScalarDecodingError.invalidType
        }

        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")

        // https://stackoverflow.com/questions/39433852/parsing-a-iso8601-string-to-date-in-swift
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"

        guard let date = formatter.date(from: raw) else {
            throw DateTimeScalarDecodingError.invalidValue
        }

        self = date
    }

    public static var mockValue = Date.now
}

enum DateTimeScalarDecodingError: Error {
    case invalidType
    case invalidValue
}

Don’t forget to add your scalar mapping to code generator options!