When a query contains a few variables with custom types, the API.swift generation may end up with a the following error:
ambiguous use of "map"
It looks like the one-liner init passing a dictionary as snapshot is the problem, I suspect it being too complex to evaluate with all the flatMap required.
Assuming the following query:
query ProjectOverview(
$projectId: ID!,
$endDate: NaiveDateTime!
$previousDate: NaiveDateTime!
) {
project(id: $projectId) {
id
key
name
clientName
lastProjectSourceScores(endDate: $endDate) {
value
source {
id
name
}
}
previousScores:lastProjectSourceScores(endDate: $previousDate) {
value
source {
id
name
}
}
events {
value
metadata {
name
content
}
sentAt
source {
name
}
}
}
}
The following init will be generated, and will raise an _ambiguous use of 'map'_ error:
public init(id: GraphQLID, key: String, name: String, clientName: String, lastProjectSourceScores: [LastProjectSourceScore?]? = nil, previousScores: [PreviousScore?]? = nil, events: [Event?]? = nil) {
self.init(snapshot: ["__typename": "Project", "id": id, "key": key, "name": name, "clientName": clientName, "lastProjectSourceScores": lastProjectSourceScores.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, "previousScores": previousScores.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, "events": events.flatMap { $0.map { $0.flatMap { $0.snapshot } } }])
}
It is possible to temporary fix the generated by extracting all the flatMap into variables, and passing those variables into the dictionary instead. That's why I suspect the generated code being too complex.
public init(id: GraphQLID, key: String, name: String, clientName: String, lastProjectSourceScores: [LastProjectSourceScore?]? = nil, previousScores: [PreviousScore?]? = nil, events: [Event?]? = nil) {
let lastProjectSourceScoresMap = lastProjectSourceScores.flatMap { $0.map { $0.flatMap { $0.snapshot } } }
let previousScoresMap = previousScores.flatMap { $0.map { $0.flatMap { $0.snapshot } } }
let eventsMap = events.flatMap { $0.map { $0.flatMap { $0.snapshot } } }
self.init(snapshot: ["__typename": "Project", "id": id, "key": key, "name": name, "clientName": clientName, "lastProjectSourceScores": lastProjectSourceScoresMap, "previousScores": previousScoresMap, "events": eventsMap])
}
This solution is not viable, as the next _build_ command with the Apollo Run Script will overwrite it again. The right way to do it would be to fix the code generation snippet.
We also just ran into this problem.
It's a classic case of the type system not being able to infer all the types inside map and flatMap. It can also be solved by explicitly typing out the types in the closures instead of using $0 and inferred return types.
Should definitely be solved in apollo-codegen. If all types were printed out, compile speed would increase drastically too, killing two birds with one stone. @martijnwalraven
@MrAlek: Sounds like a great idea!
1:- install apollo pod version 0.6.5
2:- npm uninstall -g apollo-codegen
3:- npm install -g [email protected]
the problem is because when you use apollo version 0.7.0 this version update apollocodegen at the moment when you try to run the app and this version has this problem
MexicanDeveloper
This is still a problem. Am still getting ambiguous use of 'map' with a similar structure
Can't we just make the code generator use temporary variables here please? I see no reason to slow down compilation times and create ambiguity errors like this, just so the mapping can be put on a single line…
I just released as apollo-codegen 0.18.4 and Apollo iOS 0.8.0, which contains a fix for this issue. All the Apollo iOS tests pass with the new codegen, but there might be edge cases that aren't covered.
So it would be great to hear how this is working for people!
Doesn't seem to help my ambiguous case. The source in the generator seems to be codeGeneration.ts:444, when it generates the self.init(snapshot: [ part. But I have no clue how to write the flavour of javascript it uses
self.init(snapshot: ["__typename": "DockGroup", "id": id, "name": name, "title": title, "subTitle": subTitle, "coord": coord.flatMap { $0.snapshot }, "requestFences": requestFences.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, "lockFences": lockFences.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, "docks": docks.flatMap { $0.map { $0.flatMap { $0.snapshot } } }])
replacing it with this fixes the ambiguity:
let docks_ = docks.flatMap { $0.map { $0.flatMap { $0.snapshot } } }
self.init(snapshot: ["__typename": "DockGroup", "id": id, "name": name, "title": title, "subTitle": subTitle, "coord": coord.flatMap { $0.snapshot }, "requestFences": requestFences.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, "lockFences": lockFences.flatMap { $0.map { $0.flatMap { $0.snapshot } } }, "docks": docks_])
so extracting that inline list of (flat)maps into local variables seems to make the compiler happy (and wil probably speed up compilation as well!)
It says the ambiguity is that it cant figure out which of these candidates to use:
Swift.Collection:19:17: note: found this candidate
public func map<T>(_ transform: (Self.Element) throws -> T) rethrows -> [T]
Swift.Sequence:19:17: note: found this candidate
public func map<T>(_ transform: (Self.Element) throws -> T) rethrows -> [T]
@js Are you sure you're running Apollo iOS 0.8.0? The generated code you posted does not contain the fixes in apollo-codegen 0.18.4.
There shouldn't be any $0 in the generated code, these have been replaced with explicitly typed closure parameters.
Had to clean in order to pick up the carthage changes, so now I got the type annotations.
However it still fails with the same ambiguity, and extracting it into local variables fixes it.
Also, it now generates additional errors:
'[DockGroupsQuery.Data.DockGroup?]?' is not convertible to 'DockGroupsQuery.Data.DockGroup?'
for:
public init(dockGroups: [DockGroup?]) {
self.init(snapshot: ["__typename": "Query", "dockGroups": dockGroups.map { (value: [DockGroup?]?) in value.flatMap { (value: [DockGroup?]) in value.map { (value: DockGroup?) in value.flatMap { (value: DockGroup) in value.snapshot } } } }])
}
it thinks a [DockGroup?] is of type [DockGroup?]? it seems? when generating the Data: GraphQLSelectionSet struct
@js The additional errors you're seeing should be fixed with apollo-codegen 0.18.5. You may need to reinstall it manually to get the latest version (npm install -g apollo-codegen).
Not sure what to do about the ambiguities. This seems like a compiler issue. Have you tried this with Swift 4.1 in the latest Xcode beta?
thanks 0.18.5 fixed the errors.
As for the ambiguities, I can't really understand why you're so insistent on keeping the code generation to a dictionary that is given directly to the init(snapshot:). Extracting the dictionary values into a temporary variable _will_ avoid ambiguities, and the compiler will spend less time trying to solve those very same ambiguities. win win imho, and I don't understand why you'd rather have a broken library…
@js I had hoped explicitly typing the closure parameters would solve the ambiguities (and it has other benefits of course, like faster compilation).
I'm not against introducing intermediate variables, but I'd like to better understand the scope of the issue before jumping to solutions to make sure we're solving for the right problem. If we generate intermediate variables everywhere, that would lead to a lot of extra code.
Do you have an idea why these expressions are still ambiguous even with explicit parameter types? And why intermediate variables (without type annotations) solve it?
Have you tried this in Swift 4.1?
Does this occur just with flatMap? Is extracting intermediate variables for a single level enough? How about nested flatMaps?
@MrAlek Do you have an idea what could be going on here?
@martijnwalraven I do have other types named the same as the ones apollo-codegen generates, to keep some boundary on where the graphql objects gets used, so maybe the issue is that there is another struct named Dock in the same module? Even though the apollo type is scoped as MyModule.FooQuery.Data.Foo.Bar and my type is MyModule.Bar
@martijnwalraven @js What's missing in the current generated closures is that they only specify parameter types, not return types, so the compiler still has to do a lot of type inference.
@martijnwalraven for what it's worth I'm still facing ambiguities as well with the latest version, mostly on map commands. I'll test on Swift 4.1 as soon as possible and get back with any feedbacks
@MrAlek Ah, of course, that makes sense!
@js @matteinn I just published apollo-codegen 0.18.6, which also generates return types for closures. Could you see if that fixes the ambiguities you're seeing?
works great!
@js Good to hear!
I'm not sure if this is the same issue, but after upgrading to apollo 0.8.0 and apollo-codegen 0.18.6, my API.swift file isn't compiling correctly due to the new map format.
I've created an issue over at apollographql/apollo-codegen#383 with more information.
This should be fixed in apollo-codegen 0.18.7!
Closing this out as it was fixed - please open a new issue if you are still running into problems here. Thanks!
Most helpful comment
We also just ran into this problem.
It's a classic case of the type system not being able to infer all the types inside
mapandflatMap. It can also be solved by explicitly typing out the types in the closures instead of using$0and inferred return types.Should definitely be solved in
apollo-codegen. If all types were printed out, compile speed would increase drastically too, killing two birds with one stone. @martijnwalraven