apollographql / apollo-ios

📱  A strongly-typed, caching GraphQL client for iOS, written in Swift.
https://www.apollographql.com/docs/ios/
MIT License
3.89k stars 728 forks source link

Invalid type aliases generated on recursive types #3338

Closed jmccance closed 2 months ago

jmccance commented 9 months ago

Summary

When fragments have a recursive structure, the generated type alias can become self-referencing and the module can't compile.

/Users/jmccance/Development/apollographql-typealias-references-itself/MySchema/Sources/Operations/Queries/GetLinksQuery.graphql.swift:123:28: error: type alias 'Link' references itself
          public typealias Link = Link.To.Link

Version

1.9.0

Steps to reproduce the behavior

I've confirmed this behavior on 1.8.0 and 1.9.0. It seems to occur when using interfaces and multiple fragments on that interface.

schema.graphqls ```graphqls type Query { links: [Link!]! } interface Linkable { id: ID! links: [Link!]! } interface Link implements Linkable { id: ID! to: Linkable! } type ChildLink implements Link & Linkable { id: ID! to: Linkable! } ```
GetLinks.gql ```gql query GetLinks { links { to { id links { id } } } links { ...MyLink ...MyChildLink } } fragment MyLink on Link { to { id } } fragment MyChildLink on ChildLink { id } ```

These two together generate this offending block:

        /// Link.AsChildLink.To
        ///
        /// Parent Type: `Linkable`
        public struct To: MySchema.SelectionSet {
          public let __data: DataDict
          public init(_dataDict: DataDict) { __data = _dataDict }

          public static var __parentType: ApolloAPI.ParentType { MySchema.Interfaces.Linkable }

          public var id: MySchema.ID { __data["id"] }
          public var links: [Link] { __data["links"] }

          // !!! error: type alias 'Link' references itself
          public typealias Link = Link.To.Link
        }

For a complete reproduction of the bug, take a look at https://github.com/jmccance/apollographql-typealias-references-itself.

Logs

$ ./apollo-ios-cli generate && (cd MySchema && swift build)
Building for debugging...
error: emit-module command failed with exit code 1 (use -v to see invocation)
/Users/jmccance/Development/apollographql-typealias-references-itself/MySchema/Sources/Operations/Queries/GetLinksQuery.graphql.swift:123:28: error: type alias 'Link' references itself
          public typealias Link = Link.To.Link
                           ^
/Users/jmccance/Development/apollographql-typealias-references-itself/MySchema/Sources/Operations/Queries/GetLinksQuery.graphql.swift:123:43: note: while resolving type 'Link.To.Link'
          public typealias Link = Link.To.Link
                                          ^
/Users/jmccance/Development/apollographql-typealias-references-itself/MySchema/Sources/Operations/Queries/GetLinksQuery.graphql.swift:123:28: error: type alias 'Link' references itself
          public typealias Link = Link.To.Link
                           ^
/Users/jmccance/Development/apollographql-typealias-references-itself/MySchema/Sources/Operations/Queries/GetLinksQuery.graphql.swift:123:43: note: while resolving type 'Link.To.Link'
          public typealias Link = Link.To.Link
                                          ^
error: fatalError

Anything else?

No response

AnthonyMDev commented 8 months ago

Thanks for the detailed report with reproduction case! I see what's going on here. We'll look into getting a fix out for this soon.

iAmericanBoy commented 7 months ago

Hello Anthony, any updates on this issue? This is still blocking us from updating apollo

AnthonyMDev commented 7 months ago

Hi Dominic. Thanks for letting us know that this is also blocking you. I'm working on a complex, high priority feature right now, so I'm not going to be able to get to this just yet, but I've marked this issue as high priority also and hope to dig into in as soon as I'm finished with what I'm working on. Can't give too great of an ETA until I get to research how to solve this.

iAmericanBoy commented 7 months ago

Sorry Anthony, Joel and I work together at M1. I was just trying to follow up on the status thank you for the update. And thank you for keeping an eye on this

jmccance commented 5 months ago

@AnthonyMDev Just checking in: is investigating this still on the roadmap? We're still stuck on 1.7.1 because of this issue.

AnthonyMDev commented 5 months ago

Hey there. Sorry that we haven't had the chance to get to this one yet. I'm not sure when we'll get there, but I'm thinking about it again and realizing you should be able to workaround this with field aliases.

Using a field alias will change the name of the generated types, so you can avoid the naming collision. You could change this part of your query definition to something like this:

outerLinks: links {
  to {
    innerLinks: links {
      ...
    }
  }
}

This would cause the generated code to look like this:

 /// OuterLink.AsChildLink.To
        ///
        /// Parent Type: `Linkable`
        public struct To: MySchema.SelectionSet {
          public let __data: DataDict
          public init(_dataDict: DataDict) { __data = _dataDict }

          public static var __parentType: ApolloAPI.ParentType { MySchema.Interfaces.Linkable }

          public var id: MySchema.ID { __data["id"] }
          public var innerLinks: [InnerLink] { __data["links"] }

          // !!! error: type alias 'Link' references itself
          public typealias Link = OuterLink.To.InnerLink
        }

Would something like this work for you in the interim?

jmccance commented 5 months ago

@AnthonyMDev Reviewing the code again, we found we were actually able to make some changes to remove the self-referencing field entirely. Appreciate the idea on the aliases, though! Hopefully that will help anyone else who runs into this until it can be fixed.

github-actions[bot] commented 2 months ago

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.