Comments (10)
Hi @solidcell - can you share the code you've got so far for your custom scalar?
I agree moving to 1.0 is a large migration; have you read the 1.0 migration guide that lays out everything that you may need to change during the migration?
from apollo-ios.
Yea I've already read the migration guide.
The easiest way I've found to get a first pass which compiles, and sort of works is to not use a typealias
directly on [String:Any]
, but to make it a wrapper struct. However that has its disadvantages. Here's what I've got so far:
public struct JSON: CustomScalarType, SelectionSetEntityValue {
public init(_fieldData: AnyHashable?) {
guard let _fieldData else {
fatalError()
}
self.value = _fieldData
}
public init(_jsonValue value: JSONValue) throws {
self.value = value
}
public init(anyHashable: JSONValue) {
self.value = anyHashable
}
public var _fieldData: AnyHashable {
value
}
public var _jsonValue: JSONValue {
value
}
public var _asAnyHashable: AnyHashable {
value
}
public subscript<T>(_ key: String) -> T? {
guard let dict = value as? [String : Any],
let casted = dict[key] as? T
else { fatalError() }
return casted
}
public func compactMap<T>(
_ transform: ((key: String, value: Any)) throws -> T?
) rethrows -> [T] {
guard let dict = value as? [String : Any]
else { fatalError() }
return try dict.compactMap(transform)
}
public func compactMapValues<T>(
_ transform: (Any) throws -> T?
) rethrows -> [String : T] {
guard let dict = value as? [String : Any]
else { fatalError() }
return try dict.compactMapValues(transform)
}
public var values: Dictionary<String, Any>.Values {
guard let dict = value as? [String : Any]
else { fatalError() }
return dict.values
}
public var description: String {
guard let dict = value as? [String : Any]
else { fatalError() }
return dict.description
}
private let value: JSONValue
}
I haven't refactored this up yet since I don't like the direction this solution is going. It's enabled me to get the app compiling and running again, but there are a few issues I've noticed so far:
- Needing to redefine and delegate methods (
compactMap
,subscript
, etc.) to the wrappedDictionary
is getting out of hand and isn't elegant. - It's a bit awkward that a
JSON
field from a result is treated likeJSON
wrapper type, but if it contains nested JSON itself, that nested JSON accessed withsubscript
won't technically beJSON
(the wrapper type), but just a regularAnyHashable([String : Any])
. Not the end of the world, but it's awkward that with this solution the types for top-level and nested JSON hashes are different, so you need to be careful about casting and what to expect. And I don't think wrapping it with the type explicitly upon returning insubscript
is great either.
I would much prefer to be able to simply work with the underlying [String : Any]
type as I had been doing with Apollo 0.x
. I'm hoping I can get back to that. However, trying to do that is giving me a whole host of problems. So I'm not sure if it's the better solution in the end or if it's even possible.
Doing this, for instance:
public typealias JSON = [String : Any]
extension JSON: CustomScalarType {}
Is giving me:
'CustomScalarType' requires the types 'Any' and 'any GraphQLOperationVariableValue' be equivalent
Conditional conformance of type 'Dictionary<Key, Value>' to protocol 'CustomScalarType' does not imply conformance to inherited protocol 'AnyScalarType'
Conditional conformance of type 'Dictionary<Key, Value>' to protocol 'CustomScalarType' does not imply conformance to inherited protocol 'OutputTypeConvertible'
Did you mean to explicitly state the conformance with different bounds?
Type 'Dictionary<Key, Value>' does not conform to protocol 'CustomScalarType'
I'm just starting to dive deeper, but something tells me I'm fighting against the current. The types and protocols in Apollo 1.x are a lot to take in, so I'm just trying to wrap my head around it. For instance, the first error: 'CustomScalarType' requires the types 'Any' and 'any GraphQLOperationVariableValue' be equivalent
. The compiler isn't being particularly helpful and it's not clear to me how CustomScalarType
is making that requirement in the first place. Maybe some extension being defined somewhere on Dictionary
which is influencing this conformance.. It's hard to say.
from apollo-ios.
- Needing to redefine and delegate methods (compactMap, subscript, etc.) to the wrapped Dictionary is getting out of hand and isn't elegant.
I'm not sure why you need to be defining conformance to SelectionSetEntityValue
, that might be compounding the issues. Any
as the element is not helping though, it's too broad. What about using AnyHashable
instead?
struct JSON: CustomScalarType, Hashable {
private let wrapped: [String: AnyHashable]
init(_jsonValue value: ApolloAPI.JSONValue) throws {
guard let value = value as? [String: AnyHashable] else { throw JSONDecodingError.wrongType }
self.wrapped = value
}
var _jsonValue: ApolloAPI.JSONValue { wrapped }
}
- It's a bit awkward that a
JSON
field from a result is treated likeJSON
wrapper type, but if it contains nested JSON itself, that nested JSON accessed withsubscript
won't technically beJSON
(the wrapper type), but just a regularAnyHashable([String : Any])
. Not the end of the world, but it's awkward that with this solution the types for top-level and nested JSON hashes are different, so you need to be careful about casting and what to expect. And I don't think wrapping it with the type explicitly upon returning insubscript
is great either.
Can you show me how you ideally want to be able to access the custom scalar value? I'm not sure there is any way around having to cast the type out of the subscript when using the value type of Any
, and you will always need to be careful because there is no type safety in Any
.
Custom scalars are for your code to define and the type safety you get from that is entirely up to what your custom type provides. If you want that type to be generated for you then it needs to be fully defined in the schema so codegen can work with it.
from apollo-ios.
If I don't conform to SelectionSetEntityValue
, I get this error:
Using AnyHashable
instead of Any
as the value didn't change anything (either better or worse), so I'll leave it as AnyHashable
. That was my plan, but I was trying to be as incremental in my migration as possible. So I was leaving it as Any
for now until I got everything working again, since that's what it was pre-migration.
I'm not sure there is any way around having to cast the type out of the subscript when using the value type of Any
Ah, misunderstanding here. It's not my intention to get more type safety from Any
than I should expect. I was just now trying to type up exactly what I was after, but I was on a wrong path. So nevermind my #2
.
In the end, it would just be nice to just have #1
. A typealias would allow me to stop having this need to delegate everything and just simply have the type I'm really after. Something like what's possible for Date
:
public typealias Date = Foundation.Date
extension Foundation.Date: CustomScalarType {
public init (_jsonValue value: JSONValue) throws {
guard let string = value as? String else {
throw JSONDecodingError.couldNotConvert(value: value, to: String.self)
}
guard let date = Date(jsonString: string) else {
throw JSONDecodingError.couldNotConvert(value: string, to: Date.self)
}
self = date
}
...
But using a typealias gives me:
'CustomScalarType' requires the types 'AnyHashable' and 'any GraphQLOperationVariableValue' be equivalent
Conditional conformance of type 'Dictionary<Key, Value>' to protocol 'CustomScalarType' does not imply conformance to inherited protocol 'AnyScalarType'
Conditional conformance of type 'Dictionary<Key, Value>' to protocol 'CustomScalarType' does not imply conformance to inherited protocol 'OutputTypeConvertible'
Did you mean to explicitly state the conformance with different bounds?
Type 'Dictionary<Key, Value>' does not conform to protocol 'CustomScalarType'
from apollo-ios.
@solidcell - have you managed to make any progress on this or is it still an issue? I'm wondering if the other issue we've worked on been able to help you with this one too?
from apollo-ios.
What if you change your scalar type to [String: AnyHashable]
? I think that might work out of the box. We needed to ensure the values were Hashable
in 1.0.
from apollo-ios.
Related Issues (20)
- Add support for SPM `Package.resolved` version `3`
- Support multiple schemas / endpoint in one project HOT 7
- Parity with Kotlin SDK for cache chaining and optimistic cache updates HOT 2
- PrivacyInfo.xcprivacy file is invalid for Apollo and ApolloApi. HOT 7
- SchemaConfiguration should be scoped within the namespace enum for `embeddedInTarget` module type
- Investigation: Allow codegen projects to modify generated filenames
- Add support for incremental cache writes
- Add support for incremental cache reads
- Avoid File Overwriting for Unchanged Files HOT 1
- `InMemoryNormalizedCache` is a memory bomb HOT 5
- iOS 13.0 or newer error - compile fails HOT 3
- Custom Scalar | ApolloAPI.JSONDecodingError.couldNotConvert(value: AnyHashable to: Swift.String HOT 5
- How should I continue after handleErrorAsync inside a custom ApolloErrorInterceptor? HOT 5
- Deprecate `legacyResponse` and prepare for partial/incremental caching HOT 2
- Custom scalar isn't being respected HOT 7
- @defer issue HOT 8
- Documentation for @import directive. HOT 9
- Add debugDescription api for Apollo generated types
- Circular reference Build Error HOT 6
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from apollo-ios.