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

Compilation errors when generating a mutation with NonNullable Input #185

Closed
backjo opened this issue Jul 24, 2017 · 5 comments
Closed

Compilation errors when generating a mutation with NonNullable Input #185

backjo opened this issue Jul 24, 2017 · 5 comments
Assignees

Comments

@backjo
Copy link

backjo commented Jul 24, 2017

When doing the following mutation in Swift, the generated classes do not compile

Schema:

type Author {
    id: Int!
    firstName: String
    lastName: String
    posts: [Post] # the list of Posts by this author
  }

  input AuthorInput {
    id: Int!
    firstName: String
	  lastName: String
		posts: PostInput
  }

  type Post {
    id: Int!
    title: String
    author: Author
    votes: Int
  }

  input PostInput {
    id: Int!
		firstName: String
		lastName: String
	  author: AuthorInput
  }

  # the schema allows the following query:
  type Query {
    posts: [Post]
    author(id: Int!): Author
  }

  # this schema allows the following mutation:
  type Mutation {
    upvotePost (
      postId: Int!
    ): Post
		
		createPost (
		   input: PostInput!
		) : Post
  }

Mutation

mutation x($input:PostInput!) {
    createPost(input:$input)
}

Errors:

Value type 'AuthorInput' cannot have a stored property that references itself
Value type 'PostInput' cannot have a stored property that references itself
@pranayairan
Copy link

@martijnwalraven is apollo android uses same code gen? we see the issue there as well.

@martijnwalraven
Copy link
Contributor

@pranayairan: Apollo Android uses apollo-codegen to generate an intermediate representation, but the code generation itself is written in Java. It may very well have the same issue though.

@weapon3042
Copy link

weapon3042 commented Jul 25, 2017

@martijnwalraven There are at 3 possible solutions I found for reference cycles with value types:

Solution 1: Change at least one of the structs to class, to make a reference cycle, you need at least one reference.

Solution 2: As mentioned here: indirect enums and structs, an elegant solution could be to wrap your reference inside an enum with an indirect case.

For example,

enum ReferencePin {
  case none
  indirect case pin(Pin)
}

struct DetailedPin {
     var pin: ReferencePin
}

struct Pin {
    var detailedPin: DetailedPin?
}

Indirect tells the compiler there is a recursion in your enum, and it should insert the necessary layer of indirection.

Solution 3: Create a wrapper type backed by a class instance to provide the necessary indirection:

//  Provides indirection for a given instance.
//  For value types, value semantics are preserved.
struct Indirect<T> {
    // Class wrapper to provide the actual indirection.
    private final class Wrapper {
        var value: T
        init(_ value: T) {
            self.value = value
        }
    }

    private var wrapper: Wrapper

    init(_ value: T) {
        wrapper = Wrapper(value)
    }

    var value: T {
        get {
            return wrapper.value
        }
        set {
            // upon mutation of value, if the wrapper class instance is unique,
            // simply mutate the underlying value directly.
            // otherwise, create a new instance.
            if isKnownUniquelyReferenced(&wrapper) {
                wrapper.value = newValue
            } else {
                wrapper = Wrapper(newValue)
            }
        }
    }
}

You can now just use the Indirect wrapper for one (or both) of your structs properties:

struct DetailedPin {
    var pin: Indirect<Pin>?
}

struct Pin {
    var foo: String
    var detailedPin: DetailedPin?
}


var p = Pin(foo: "foo", detailedPin: nil)
var d = DetailedPin(pin: Indirect(p))
p.detailedPin = d

@martijnwalraven
Copy link
Contributor

This should be fixed in apollo-codegen 0.16.2.

@weapon3042: Thanks for looking into solutions for avoiding reference cycles with value types. In this case, the easiest solution seemed to be to replace stored properties with computed properties, because the end result should be a graphQLMap anyway.

@weapon3042
Copy link

@martijnwalraven Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants