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

How to create Query.Data without getting it through the AppoloClient? #51

Closed
nuke-dash opened this issue Feb 6, 2017 · 14 comments
Closed

Comments

@nuke-dash
Copy link

With Apollo Codegen an API.swift file is generate with contains the data structs.
But how can you create a struct without getting it through the AppoloClient as there is only an init which accepts a GraphQLResultReader?
I'm looking to create the data in a unit testing scenario without having to get it through the AppoloClient.
I tried creating a mock AppoloStore or a GraphQLResultReader but that doesn't seem possible as the init or other methods are inaccessible.

Take for example following API.swift file.
How would you create a Channel?

//  This file was automatically generated and should not be edited.

import Apollo

public final class ProofOfConceptQuery: GraphQLQuery {
  public static let operationDefinition =
    "query ProofOfConcept($profileId: ID!) {" +
    "  initialChannelList(profileId: $profileId) {" +
    "    id" +
    "    title" +
    "    channels {" +
    "      id" +
    "      title" +
    "    }" +
    "  }" +
    "}"

  public let profileId: GraphQLID

  public init(profileId: GraphQLID) {
    self.profileId = profileId
  }

  public var variables: GraphQLMap? {
    return ["profileId": profileId]
  }

  public struct Data: GraphQLMappable {
    public let initialChannelList: InitialChannelList?

    public init(reader: GraphQLResultReader) throws {
      initialChannelList = try reader.optionalValue(for: Field(responseName: "initialChannelList", arguments: ["profileId": reader.variables["profileId"]]))
    }

    public struct InitialChannelList: GraphQLMappable {
      public let __typename = "ChannelList"
      public let id: GraphQLID
      public let title: String
      public let channels: [Channel?]

      public init(reader: GraphQLResultReader) throws {
        id = try reader.value(for: Field(responseName: "id"))
        title = try reader.value(for: Field(responseName: "title"))
        channels = try reader.list(for: Field(responseName: "channels"))
      }

      public struct Channel: GraphQLMappable {
        public let __typename = "Channel"
        public let id: GraphQLID
        public let title: String

        public init(reader: GraphQLResultReader) throws {
          id = try reader.value(for: Field(responseName: "id"))
          title = try reader.value(for: Field(responseName: "title"))
        }
      }
    }
  }
}
@martijnwalraven
Copy link
Contributor

I'd be interested in hearing more about how people intend to use query results with tests, and see how we can make this easier. It may make sense to also generate memberwise constructors for these structs.

For now, the easiest way is probably to create a GraphQLResponse object and use parseResult() to create a Query.Data struct. See this test case for an example.

@nuke-dash
Copy link
Author

Thanks for the quick response.
I tried using what you suggested but it doesn't seem possible.

'parseResult' is inaccessible due to 'internal' protection level

And yes generating memberwise constructors would be much more convenient than having to parse json to create a struct.

@martijnwalraven
Copy link
Contributor

@Nuke-: Make sure to update to the most recent version, because parseResult was only made public today.

@nuke-dash
Copy link
Author

That made it work. Thanks.

@jbouaziz
Copy link

I'm also having the error saying that parseResult is inaccessible. Is there been an other way to get data using a local json file?

import Foundation
import Apollo

class MyGraphQL {
    
    func getInbox() {
        guard let rootObject = json(forFileName: "inbox") else { return }
        let query = InboxQuery()
        let response = GraphQLResponse(operation: query, body: rootObject)
        
        let (result, _) = try response.parseResult().await()
    }
    
    private func json(forFileName fileName: String) -> JSONObject? {
        guard let file = Bundle.main.path(forResource: fileName, ofType: "json"),
            let string = try? String(contentsOfFile: file, encoding: .utf8),
            let data = string.data(using: .utf8),
            let object = try? JSONSerialization.jsonObject(with: data, options: []) else {
                return nil
        }
        return object as? JSONObject
    }
}

@martijnwalraven
Copy link
Contributor

@jbouaziz: You should be able to use InboxQuery.Data(jsonObject:) for that.

@jbouaziz
Copy link

It works thanks @martijnwalraven.

For those who come here, the exact method name is InboxQuery.Data(snapshot: your_json).

@martijnwalraven
Copy link
Contributor

martijnwalraven commented Oct 18, 2017

@jbouaziz: No, the snapshot constructor is not actually meant to be public! It doesn't do any type checking / conversion and can lead to crashes. So you should use InboxQuery.Data(jsonObject:) instead.

@jbouaziz
Copy link

My bad, it didn't show up in the autocompletion. Thanks!

@pittNearsoft
Copy link

Hi, Im having issues like "Auto-Linking framework not found Apollo" when I try to use Data(jsonObject:)
screen_shot_2018-05-04_at_12_04_53_pm

I have googled it and not answer for this...

@marinofelipe
Copy link

marinofelipe commented May 22, 2018

I'm using 0.8 version and parseResult() still isn't public. I had to unlock the Pod and turn it into public to be able to test. Is there another way? GraphQLResponse is a struct and it's init method isn't public as well. Thanks!

@martijnwalraven
Copy link
Contributor

@marinofelipe You shouldn't have to use parseResult for that, you can use Query.Data(jsonObject:) to create the data and use that to initialize GraphQLResult if that's what you need.

@marinofelipe
Copy link

marinofelipe commented May 22, 2018

@martijnwalraven Thanks for your fast reply. I can't initialise GraphQLResult due to internal level protection.

@marinofelipe
Copy link

If this protection have to exist, what do you think about making GraphQLResult adopt a protocol? This way we would have an interface to give us more flexibility.

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

5 participants