question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Swift Codegen, fragments

See original GitHub issue

Question/Feature request regarding the output of the generated code for swift when fragments are used.

We have a fragment called “FormattedText” to encapsulate complex text fields that have nested bold/italic/link content. And example quest would be

...
...
  headingText {
        ...formattedText
      }
...

The returned data is as follows

"headingText": {
            "spans": [
              {
                "text": "Credit card utilization"
              }
            ]
          },

The issue arrises with the gerenated code:

...
public let headingText: HeadingText?
...


  public struct HeadingText: GraphQLMappable {
                    public let __typename = "FormattedText"

                    public let fragments: Fragments

                    public init(reader: GraphQLResultReader) throws {
                        let formattedText = try FormattedText(reader: reader)
                        fragments = Fragments(formattedText: formattedText)
                    }

                    public struct Fragments {
                        public let formattedText: FormattedText
                    }
                }
...
...
//and finally Fragmented Text

public struct FormattedText: GraphQLNamedFragment {
    public static let fragmentDefinition =
        "fragment formattedText on FormattedText {" +
        "  spans {" +
        "    text" +
        "  }" +
        "}"

    public static let possibleTypes = ["FormattedText"]

    public let __typename = "FormattedText"
    public let spans: [Span?]?

    public init(reader: GraphQLResultReader) throws {
        spans = try reader.optionalList(for: Field(responseName: "spans"))
    }

    public struct Span: GraphQLMappable {
        public let __typename = "FormattedSpan"
        public let text: String?

        public init(reader: GraphQLResultReader) throws {
            text = try reader.optionalValue(for: Field(responseName: "text"))
        }
    }
}

In order to access this object you would write:

data.headingText?.fragments.formattedText

The feedback is the fragments contatiner necessary? Should the HeadingText class itself not be a FormattedText or more closely implement the FormattedText fragment. This indirection strikes me as unnecessary.

Even the headingText?.__typename == “FormattedText” so this type has been fully specified.

eg:

headingText	CKGraphQL.TestCreditFactorsQuery.Data.CreditFactor.HeadingText?	some
	__typename	String	"FormattedText"			
	fragments	CKGraphQL.TestCreditFactorsQuery.Data.CreditFactor.HeadingText.Fragments	
		formattedText	CKGraphQL.FormattedText	
			__typename	String	"FormattedText"	
			spans	[CKGraphQL.FormattedText.Span?]?	1 value	some
				[0]	CKGraphQL.FormattedText.Span?	some
					__typename	String	"FormattedSpan"	
					text	String?	"Credit card utilization"	some

So my suggestion is, can we make HeadingText more directly inherit from FormattedText?

 public struct HeadingText: FormattedText {

What are the conditions under which this fragment structure becomes more useful?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:12 (11 by maintainers)

github_iconTop GitHub Comments

2reactions
martijnwalravencommented, Apr 24, 2017

Just came across this, sounds like it might solve our issues if it lands!

https://github.com/apple/swift/pull/8718

1reaction
rwecommented, Apr 22, 2017

Hmm, @martijnwalraven in the example above, just using an associated type should correctly constrain things and make it more ergonomic. The following compiles correctly:

public enum Episode: String {
  case newhope = "NEWHOPE"
  case empire = "EMPIRE"
  case jedi = "JEDI"
}

class HeroQuery {
  struct Data {
    let hero: Hero
    struct Hero: HeroAndFriendsNames  {
      let name: String
      let friends: [Friend]

      struct Friend: HeroAndFriendsNames_Friend {
        let name: String
        let appearsIn: [Episode]
      }
    }
  }
}

protocol HeroAndFriendsNames {
  associatedtype Friend: HeroAndFriendsNames_Friend

  var name: String { get }
  var friends: [Friend] { get }
}

protocol HeroAndFriendsNames_Friend {
  var name: String { get }
}

Used like so:

let x = HeroQuery.Data(
  hero: HeroQuery.Data.Hero(
    name: "Foo",
    friends: [
      HeroQuery.Data.Hero.Friend(
        name: "Bar",
        appearsIn: [.newhope]
      )
    ]
  )
)
print(x)
print(x.hero.friends[0].appearsIn[0])

// Types are correctly constrained (and can be genericized for fragments):
func hiFromHeroAndFriendsFragment<H: HeroAndFriendsNames>(hero: H) -> String {
  let friends = hero.friends.map { $0.name }.joined(separator: ", ")
  return "\(hero.name) says, 'Hi, \(friends)!"
}

// Can be specific to the concrete structure if preferred:
func hiFromHeroQueryHero(hero: HeroQuery.Data.Hero) -> String {
  let friends = hero.friends.map { $0.name }.joined(separator: ", ")
  return "\(hero.name) says, 'Hi, \(friends)!"
}

print(hiFromHeroAndFriendsFragment(hero: x.hero))
print(hiFromHeroQueryHero(hero: x.hero))
Read more comments on GitHub >

github_iconTop Results From Across the Web

Running code generation in Swift code - Apollo GraphQL Docs
Running code generation in Swift code. For most use cases, the Apollo iOS Codegen CLI is the recommended method for generating code and...
Read more >
Unleash the power of Fragments with GraphQL Codegen
Have you ever deleted component code and forgot to delete the corresponding operation or fragment definitions that are in some other field?
Read more >
Apollo iOS codegen: Nested use of a fragment in a mutation ...
Fragments struct should be of the User fragment type, but the type is shadowed by the local ExchangePinMutation.Data.ExchangePinForToken.
Read more >
Using GraphQL queries with Apollo Client iOS SDK
Note: If you have pieces of data you may want to reuse in multiple places, make use of fragments. Next, we will generate...
Read more >
Apollo iOS tutorial - Contentful
Note: Use one GraphQL fragment for each content type in your .graphql files so that there is ... apollo codegen:generate --target=swift --schema=schema.json ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found