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

Custom Scalar | ApolloAPI.JSONDecodingError.couldNotConvert(value: AnyHashable to: Swift.String #3367

Closed
PareshPatel721 opened this issue Apr 16, 2024 · 5 comments
Labels
question Issues that have a question which should be addressed

Comments

@PareshPatel721
Copy link

Question

I'm using Apollo 1.9.3 with local package support.
So there are no any run script is needed to set in build phase.
When i invoke My API from graphql server I'm getting error as ApolloAPI.JSONDecodingError.couldNotConvert(value: AnyHashable to: Swift.String

Local framework default generate JSON file in CustomScalar folder and file only contains
public typealias JSON = String

@PareshPatel721 PareshPatel721 added the question Issues that have a question which should be addressed label Apr 16, 2024
@calvincestari
Copy link
Member

Hi @PareshPatel721 - since you mention the build phase script it sounds like you're familiar with the old 0.x versions. 1.0 has changed code generation quite a bit. We have a 1.0 migration guide and there is a specific section about custom scalars.

tl;dr - you need to write the custom scalar code yourself, just like before but in a different place now.

Copy link
Contributor

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo iOS usage and allow us to serve you better.

@PareshPatel721
Copy link
Author

PareshPatel721 commented Apr 17, 2024

I've written custom scalar code, but it still not working
public typealias JSON = String (this is what i get default implementation)

In same file I've try to extend many ways
like below

public typealias JSON = String

`extension JSON: JSONDecodable {
public init(jsonValue value: JSONValue) throws {
guard let dictionary = value as? Dictionary else {
throw JSONDecodingError.couldNotConvert(value: value, to: Dictionary.self)
}

self = dictionary

}
}`

`extension SchemaConfiguration {
public enum JSON: CustomScalarType, Hashable {
case dictionary([String: AnyHashable])
case array([AnyHashable])

    public init(_jsonValue value: JSONValue) throws {
        if let dict = value as? [String: AnyHashable] {
            self = .dictionary(dict)
        } else if let array = value as? [AnyHashable] {
            self = .array(array)
        } else {
            throw JSONDecodingError.couldNotConvert(value: value, to: JSON.self)
        }
    }

    public var _jsonValue: JSONValue {
        switch self {
        case let .dictionary(json as AnyHashable),
             let .array(json as AnyHashable):
            return json
        }
    }

    public static func == (lhs: JSON, rhs: JSON) -> Bool {
        lhs._jsonValue == rhs._jsonValue
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(_jsonValue)
    }
}

}`

`extension Schema {
public enum JSON: CustomScalarType, Hashable {
case dictionary([String: AnyHashable])
case array([AnyHashable])

    public init(_jsonValue value: JSONValue) throws {
        if let dict = value as? [String: AnyHashable] {
            self = .dictionary(dict)
        } else if let array = value as? [AnyHashable] {
            self = .array(array)
        } else {
            throw JSONDecodingError.couldNotConvert(value: value, to: JSON.self)
        }
    }

    public var _jsonValue: JSONValue {
        switch self {
        case let .dictionary(json as AnyHashable),
             let .array(json as AnyHashable):
            return json
        }
    }

    public static func == (lhs: JSON, rhs: JSON) -> Bool {
        lhs._jsonValue == rhs._jsonValue
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(_jsonValue)
    }
}

}`

`extension JSON: JSONDecodable, JSONEncodable {
public init(jsonValue value: JSONValue) throws {

    let string = value as? String
    
    if (string == nil) {
        do {
            let data1 =  try JSONSerialization.data(withJSONObject: value, options: JSONSerialization.WritingOptions.prettyPrinted) // first of all convert json to the data
            let convertedString = String(data: data1, encoding: String.Encoding.utf8) // the data will be converted to the string
            if (convertedString == nil) {
                throw JSONDecodingError.couldNotConvert(value: value, to: String.self)
            } else {
                self = convertedString ?? ""
            }
        } catch let myJSONError {
            print(myJSONError)
            throw JSONDecodingError.couldNotConvert(value: value, to: String.self)
        }
    } else {
        self = string ?? ""
    }
}
public var jsonValue: JSONValue {
    return self
}

}`

` extension SchemaConfiguration {
public enum JSON: CustomScalarType, Hashable {
case dictionary([String: AnyHashable])
case array([AnyHashable])

    public init(_jsonValue value: JSONValue) throws {
        if let dict = value as? [String: AnyHashable] {
            self = .dictionary(dict)
        } else if let array = value as? [AnyHashable] {
            self = .array(array)
        } else {
            throw JSONDecodingError.couldNotConvert(value: value, to: JSON.self)
        }
    }

    public var _jsonValue: JSONValue {
        switch self {
        case let .dictionary(json as AnyHashable),
             let .array(json as AnyHashable):
            return json
        }
    }

    public static func == (lhs: JSON, rhs: JSON) -> Bool {
        lhs._jsonValue == rhs._jsonValue
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(_jsonValue)
    }
}

} `

Please help me out where and how i can write custom scalar code, so my response get automatically parsed and confirmed to custom scalar

Thank you

@calvincestari
Copy link
Member

Codegen is generating public typealias JSON = String because that is the default for custom scalars in 1.0. We use String because that is most likely the data type used to transport the data in the JSON payload and simple execution of the default generated operation model would succeed.

You are able to edit this file and the codegen engine will not overwrite it during the next code generation. So, if your custom scalar is not a String then you must edit the code in that file and write the new code for your custom scalar. All this information can be found in our documentation on Custom Scalars.

Firstly you need to delete public typealias JSON = String since your custom scalar is not String.

Of the code you posted above this one looks correct but you might not need to put it inside an extension depending on how you created your schema module:

public enum JSON: CustomScalarType, Hashable {
  case dictionary([String: AnyHashable])
  case array([AnyHashable])

  public init(_jsonValue value: JSONValue) throws {
    if let dict = value as? [String: AnyHashable] {
      self = .dictionary(dict)
    } else if let array = value as? [AnyHashable] {
      self = .array(array)
    } else {
      throw JSONDecodingError.couldNotConvert(value: value, to: JSON.self)
    }
  }

  public var _jsonValue: JSONValue {
    switch self {
    case
      let .dictionary(json as AnyHashable),
      let .array(json as AnyHashable):
      return json
    }
  }

  // You don't need to manually define `Equatable` and `Hashable` conformance the compiler will synthesize that for you.
}

@PareshPatel721
Copy link
Author

@calvincestari
That is working
It saves my million of time
Thank you for your time and effort

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Issues that have a question which should be addressed
Projects
None yet
Development

No branches or pull requests

2 participants