GithubHelp home page GithubHelp logo

apollographql / apollo-ios Goto Github PK

View Code? Open in Web Editor NEW
3.8K 86.0 697.0 189.65 MB

๐Ÿ“ฑ ย A strongly-typed, caching GraphQL client for iOS, written in Swift.

Home Page: https://www.apollographql.com/docs/ios/

License: MIT License

Swift 99.24% Ruby 0.55% Shell 0.19% Makefile 0.02%
swift graphql-client apollo-ios ios graphql apollographql

apollo-ios's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

apollo-ios's Issues

Differentiate between explicit and implicit nil fields

I have mutation that takes a EventCreateInput where every field is optional. Setting different fields and the combination thereof provides the graphql server with the information to run the mutation. But the problem is that providing null for certain fields causes the server to behave differently from not including the field at all.

Example:

public struct EventCreateInput: GraphQLMapConvertible {
  public var graphQLMap: GraphQLMap

  public init(clientMutationId: String? = nil, type: String? = nil, consumedBy: GraphQLID? = nil, content: GraphQLID? = nil, deviceIds: [String?]? = nil, firstInteraction: String? = nil, lastInteraction: String? = nil, progress: Double? = nil, reaction: GraphQLID? = nil, shares: [ShareCreateInput?]? = nil) {
    graphQLMap = ["clientMutationId": clientMutationId, "type": type, "consumedBy": consumedBy, "content": content, "deviceIds": deviceIds, "firstInteraction": firstInteraction, "lastInteraction": lastInteraction, "progress": progress, "reaction": reaction, "shares": shares]
  }
}

The events are interactions with an item. Multiple events are sent for a single item. When providing an id for reaction the server registers the reaction with that item. To remove the reaction from that item I need to give this input to the mutation:

[
   "content":"contentID", 
   "reaction": null
]

But this is not possible since we cannot differentiate between a parameter being explicitly set to nil and one being left out when creating EventCreateInput so it takes the default value of nil.

Serializing an array using a `JSONObject` and `GraphQLResultReader`

Hey,

I am in a position where I need to bridge the two worlds between custom, native objects and Apollo generated objects.

So at some point one of my native objects has a [LinkedIdentity]? property where linkedIdentity is actualy a typealias to Apollo's generated GetLinkedIdentitiesQuery.Data.Me.LinkedIdentity.Record struct.

This native object is getting serialized using Alamofire and custom serializers. So at some point I also need to serialize the [LinkedIdentity]? array from the received json representation that Alamofire gives me.

I know I am supposed to initialise a GraphQLResultReader at some point so I am doing something like let graphQLResultReader = GraphQLResultReader(rootObject: linkedIdentitiesArray as! JSONObject)

How can I utilize this graphQLResultReader object to return me [LinkedIdentity] ?

"Use of undeclared type" on code generation for nested input objects

I have a fairly complex input schema, sometimes depending on 2 or 3 nested input objects. When I generate a swift API for this, I end up with an API.swift file with errors, complaining about a few (nested and seemingly random) input types it hasn't defined. Here's an example of the Swift output, interstingly the offending declarations are listed right at the top. Important to note that all these input objects are correctly defined and the API works fine in standalone testing.

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

import Apollo

public struct BookInput: GraphQLMapConvertible {
  public let writtenBy: WrittenByInput     // <= ERROR: Use of undeclared type 'WrittenByInput`
  public let name: String

  public var graphQLMap: GraphQLMap {
    return ["writtenBy": writtenBy, "name": name]
  }
}

public struct PublishedByInput: GraphQLMapConvertible {
  public let publisher: IdInput     // <= ERROR: Use of undeclared type 'IdInput`

  public var graphQLMap: GraphQLMap {
    return ["publisher": publisher]
  }
}

public final class ListBooksQuery: GraphQLQuery {
  public static let operationDefinition =
    "query ListBooks {" +
    "  books {" +
    "    id" +

versions: Apollo 0.4.1, apollo-codegen 0.9.1

Incorrect keys generation

I have troble with code generation.

I have in my input field with name "confirmation_token"

schema.json snippet

{
             "name": "confirmation_token",
             "description": null,
             "type": {
               "kind": "NON_NULL",
               "name": null,
               "ofType": {
                 "kind": "SCALAR",
                 "name": "String",
                 "ofType": null
               }
             },
             "defaultValue": null
           },

but when code generate object I get wrong keys

public struct setUserDetailsInput: GraphQLMapConvertible {
  public var graphQLMap: GraphQLMap

  public init(clientMutationId: String? = nil, confirmationToken: String, username: String, firstName: String, gender: String, language: String, birthday: String, zipCode: String, avatarFilename: String? = nil, avatar: String? = nil, interests: [String?]? = nil) {
    graphQLMap = ["clientMutationId": clientMutationId, **"confirmationToken": confirmationToken**, "username": username, "firstName": firstName, "gender": gender, "language": language, "birthday": birthday, "zipCode": zipCode, "avatarFilename": avatarFilename, "avatar": avatar, "interests": interests]
  }
}

GraphQLQueryWatcher may try to fetch data again even though it's already fetching data

Hi,

I found when sending multiple ApolloClient.watch() commands at the same time, the first one that finishes can trigger other watchers to fetch again even though they may not have finish fetching for the first time.

After ApolloClient.send() command finish fetching, the store publishes keys that have been changed to its subscribers.

In GraphQLQueryWatcher, line 41:

  func store(_ store: ApolloStore, didChangeKeys changedKeys: Set<CacheKey>, context: UnsafeMutableRawPointer?) {
    if context == &self.context { return }
    
    if let dependentKeys = dependentKeys, dependentKeys.isDisjoint(with: changedKeys) {
      return
    }
    
    fetch(cachePolicy: .returnCacheDataElseFetch)
  }

The Watcher may at this point not have dependentKeys set to it because it haven't finish fetching the query for the first time. The if let check here doesn't guard against that case, and causes the watcher to send out a duplicate fetch command.

Generate Apollo GraphQL API phase not executing properly

I am having trouble getting the build phase execute prperly. I have properly created the build script and using default paths for my *.graphql and schema.json. It looks like Xcode is running the generation phase but no API.swift file is generated.

Any directions on how to debug this?

Using Apollo 0.3.1 and apollo-codegen 0.8.2

GraphQLResultNormalizer doesn't match GraphQLResultReader's behavior when parsing nil value for optional fields

Hi,

I've been running into an issue where my GraphQL response includes an optional field with a null value cannot be parsed by GraphQLResultReader from cache. The Record entry created by GraphQLResultNormalizer has a nil value, which cannot be parsed by GraphQLResultReader.

In the following example GraphQL response, avatarThumbnailUrl is an optional string field, which has null value for this node.

{
	"data": {
		"node": {
			"supporters": {
				"edges": [{
					"node": {
						"avatarThumbnailUrl": null
					}
				}]
			}
		}
	}
}

In GraphQLResultNormalizer.swift at line 47

func didParseNull() {
    valueStack.append(nil)
  }

Upon parsing a nil value, the result normalizer pushes a nil value on the valueStack, which is set to the record's value for cacheKey at line 35

  func didResolve(field: Field, info: GraphQLResolveInfo) {
    path.removeLast()
    
    let value = valueStack.removeLast()
    
    let dependentKey = [currentRecord.key, field.cacheKey].joined(separator: ".")
    dependentKeys.insert(dependentKey)
    
    currentRecord[field.cacheKey] = value
    
    if recordStack.isEmpty {
      records.merge(record: currentRecord)
    }
  }

When parsing from cache, GraphQLResultReader tries to parse the optional field from the record using the optional function in JSON.swift at line 48.

func optional(_ optionalValue: JSONValue?) throws -> JSONValue? {
  guard let value = optionalValue else {
    throw JSONDecodingError.missingValue
  }
  
  if value is NSNull { return nil }
  
  return value
}

This function throws missingValue error if the value is nil.

I believe this is a bug because this behavior causes these type of GraphQL responses to be not readable from cache.

I found by changing the nil value that was pushed onto the valueStack in GraphQLResultNormalizer to NSNull(), it fixes the problem.

func didParseNull() {
    valueStack.append(NSNull())
  }

More descriptive errors

Right now JSONDecodingError contains why JSON decoding failed but is missing some potentially useful debugging information.

When there is a missing value for a key, it doesn't show you where in the parent JSON the value is missing. Without the parent JSON it's hard to figure out which object is missing the value if you're using the same key in different objects (e.g. id field in two different objects).

Similarly, when an error occurs converting a JSON value to an expected type, it would also be useful to know the key and parent JSON in which the error occurred. Without this information, it's not easy to tell which part of the response was malformed.

Do you think an enhancement to add such information (e.g. key, localized malformed JSON) is possible or useful?

Fragment doesn't get code generated when embedded in inline fragment with same type information

In the following example:

query Details($id: ID!) {
  viewer {
    Model(id: $clipId) {
      id
      ...BasicInfo
      recommendations {
        edges {
          node {
            id
            ... on ModelInterface {
              ...BasicInfo
            }
          }
        }
      }
    }
  }
}

fragment BasicInfo on ModelInterface {
  title
}

we get the following code generation:

public struct Recommendation: GraphQLMappable {
  public let __typename = "XXXConnection"
  public let edges: [Edge?]?

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

  public struct Edge: GraphQLMappable {
    public let __typename = "XXXEdge"
    public let node: Node?

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

    public struct Node: GraphQLMappable {
      public let __typename: String
      public let id: GraphQLID

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

The type constraint for the conditional fragment to be a ModelInterface is redundant but the fragment should still be included in the generated code.

GraphQLResponseError not publicly available

I'm currently working with a GraphQL that is protected by an authentication token in the request headers.

I've configured the HTTPNetworkTransport to use the token and all works well - except when / if the said token gets invalidated, in which case any subsequent GraphQL request fail with a GraphQLResponseError.

The response error does contain the info I need to differentiate an Unauthorized error from any other - but as its not publicly declared we can only access its errorDescription property via. its LocalizedError conformance from outside the Apollo module.

Would it make sense to make GraphQLResponseError public, or at least parts of it?

I understand that it may break the encapsulation of future network transports (which probably wouldn't leverage HTTPURLResponse).

Of course parsing the errorDescription seems a tad counter-productive here :)

#jsonValue does not recurse

I run into a crash when I try to send a query with nested inputs.

'NSInvalidArgumentException', reason: 'Invalid type in JSON write (_SwiftValue)'

I can't really tell what's going on, but my first investigations tell me that this isn't correct:

BookInput(author: AuthorInput(id: "123")).jsonValue //=> ["author": AuthorInput(id: "123")]

API.swift generated class is empty

I have followed the steps on the installation page and have a compiling project. I have also imported the proper schema.json from my server, but the script seems to generate a blank API.swift file. Am I missing something here?

I am using Cocoapods to install the latest version (0.3.1 I believe), and have installed apollo-codegen from npm. Any help would be appreciated!

Undeclared type โ€“ย  GraphQLID

Hi guys,

I've been trying to use MyAPI playground from quick-start package.
I did introspection query and then re-built target and got this error:
http://take.ms/5AFsd
Using latest apollo-codegen.

Here's how this field is declared in schema.json:

      {
        "kind": "OBJECT",
        "name": "AccessPoint",
        "description": null,
        "fields": [
          {
            "name": "id",
            "description": null,
            "args": [],
            "type": {
              "kind": "SCALAR",
              "name": "ID",
              "ofType": null
            },
            "isDeprecated": false,
            "deprecationReason": null
          },

Public access to jsonObject on GraphQLMap

Hey.
Could you provide public access to the GraphQLMap's jsonObject property? I try to use apollo-ios with another networking library, so I'll need to have the parameters unencoded.
For this I extract the information from the query/mutation:

[
    "query": type(of: query).queryDocument,
    "variables": query.variables?.dict ?? []
]

The dict is a computed property on GraphQLMap which parses the description back to a dictionary (hacky). This is just unnecessary overhead, because I could use the jsonObject directly. Since it's private clients can't change it anyway :)

Public initializers for auto-generated code

Hi,

Can we have public initializers for auto-generated code.

It becomes very tedious to write unit test and creating Mocks for the models.

Based off of the discussion here.

#49

Thanks.

Subscriptions

Hi, I am trying to develop against a GraphQL server which heavily relies on subscriptions. I would love to not have to use the Javascript Apollo Client in React Native, but cannot seem to figure out if there is an already included way to implement subscriptions with this library. If there is not, is there possibly another Swift/Objective-C library which has this capability?

Query without variables throws fatal error

My app crashes when I use a query without any variables.
Below you can see my query:

query Me {
    me {
        ...UserFragment
    }
}

When executing this query I get the following Swift error:

fatal error: unexpectedly found nil while unwrapping an Optional value

This is happening on line 63 in NetworkTransport.swift where operation.variables! is being forcefully unwrapped.
let stringVar = try! serializationFormat.serialize(value: operation.variables!)

My workaround is to make an extension for MeQuery and return a empty Dictionary for variables.

public extension MeQuery {
    public var variables: GraphQLMap? {
        return [:]
    }
}

When I use this extension the query works and the app no longer crashes.

I'm using Apollo 0.4.2 installed with cocoapods.

Incorrect code generation: String instead of Int

Steps to reproduce:

The resulting API.swift that was generated using apollo-ios version 0.4 and apollo-codegen version 0.9 contains: public let centAmount: String (MoneyDetails struct property). The actual type from the schema:

{
"name": "centAmount",
"type": {
  "kind": "NON_NULL",
  "name": null,
  "ofType": {
    "kind": "SCALAR",
    "name": "Long",
    "ofType": null
  }
}

Current limitations in GraphQLError

Thanks to all who have contributed to this fantastic library.

It would be great to be able to be able to have more flexibility when reading from a GraphQL response's errors array. Specifically:

  1. The GraphQL spec calls out locations as an key in a GraphQL error in addition to message. It seems straight forward add a locations key to GraphQLError.
  2. Although the spec doesn't specifically mention other error keys, it would be useful to be able to parse additional keys (ex: userMessage -- a message that's guaranteed to be presentable to the user). To do so, it seems like adding an additional type parameter to GraphQLResult resulting in GraphQLResult<Data, ErrorType> (sadly, plain Error already exists in the namespace), thus allowing consumers of apollo-ios to define their own error types. This is clearly a more involved change, but is crucial to my own usage of apollo-ios.

Lastly, an certainly least importantly, the aforelinked spec section also mentions:

If the data entry in the response is null or not present, the errors entry in the response must not be empty. It must contain at least one error. The errors it contains should indicate why no data was able to be returned.

Thus, it seems like an enum might be better suited for this task rather than data and errors both being optionals. Result is a good example of this, and on potential implementation would be for GraphQLError to have a property of type Result.

.sh file in build output prevents signing

In the latest versions of Xcode, any file in the build output starting with an #! will result in an error when attempting to sign your app and upload to the store (as documented in this stackoverflow question). This is problematic because the Apollo deploys a the check-and-run-apollo-codegen.sh script to the build folder which is used in the build process, and the error when publishing just indicates that an object was not signed provides nothing to indicate what the problem is.

To work around this problem, I added a line at the end of the build step given in the installation documentation that removes the .sh file after codegen was completed:

APOLLO_FRAMEWORK_PATH=$(eval find $FRAMEWORK_SEARCH_PATHS -name "Apollo.framework" -maxdepth 1)

if [ -z "$APOLLO_FRAMEWORK_PATH" ]; then
echo "error: Couldn't find Apollo.framework in FRAMEWORK_SEARCH_PATHS; make sure to add the framework to your project."
exit 1
fi

cd ${SRCROOT}/${TARGET_NAME}
$APOLLO_FRAMEWORK_PATH/check-and-run-apollo-codegen.sh generate $(find . -name '*.graphql') --schema schema.json --output API.swift

rm $APOLLO_FRAMEWORK_PATH/check-and-run-apollo-codegen.sh

It would be very beneficial to anyone who wants to publish their app if you could update the documentation to include the last line of code, and/or set the build script up in a way that does not result in an .sh file being put in the build output.

AnyGraphQLQuery?

Is it possible to create an AnyGraphQLQuery class?
I'm having some troubles/difficulties that you cannot assign a GraphQLQuery to a variable without knowing its concrete type.

So I tried building a type erasure but that doesn't work because the protocol contains a static var static var operationDefinition
Is it possible to not make it a static var and just a regular var?
Because it seems to be only used in HTTPNetworkTransport
And there is doesn't seem to be really necessary for it to be a static variable.

let body: GraphQLMap = ["query": type(of: operation).queryDocument, "variables": operation.variables]

Apollo integration issue

The version of Apollo.framework in your project requires the use of version 0.9 of apollo-codegen, but an unknown older version seems to be installed.

Why it is happened? What I should for resolve this issue?

Code signing problems due to Apollo framework

I just finished debugging a bunch of problems I was having submitting a build to TestFlight, and the root of the cause seems to have been the check-and-run-apollo-codegen.sh file in the Apollo framework (installed via Cocoapods). I removed that file from the project and temporarily removed the run script from my own project build phases, and the code signing finally succeeded. Here is the error I got:

ERROR ITMS-90035: "Invalid Signature. Code object is not signed at all. Make sure you have signed your application with a distribution certificate, not an ad hoc certificate or a development certificate. Verify that the code signing settings in Xcode are correct at the target level (which override any values at the project level). Additionally, make sure the bundle you are uploading was built using a Release target in Xcode, not a Simulator target. If you are certain your code signing settings are correct, choose "Clean All" in Xcode, delete the "build" directory in the Finder, and rebuild your release target. For more information, please consult https://developer.apple.com/library/ios/documentation/Security/Conceptual/CodeSigningGuide/Introduction/Introduction.html"

Hopefully this will help any one else having this problem

Objective-C bridging

I'd like to partly re-open the discussion on #1 ๐Ÿ™ˆ

I understand that it makes the most sense to create a new codegen target for an Objective-C only environment instead of crippling the beautiful Swift version. One thing we came across though, is the need to have some kind of Objective-C bridging. I guess other projects, at least older ones, will come across this, too.

The goal is to write most part in Swift, but there are still some parts of the code/UI that are too costly to rewrite and that need data from the backend.

One general solution to expose model objects to Objective-C code would be to pass dictionaries, instead of the generated GraphQLNamedFragment structs to @objc functions:

extension GraphQLNamedFragment {
  func dictionary() -> [String:Any]? {
    // Note: this would be simpler, if the parsed dictionary would be kept/exposed
    return try? wrap(self) // See: https://github.com/JohnSundell/Wrap
  }
}

// Function that can be called from Objective-C with a result of Array<Dictionary<String,Any>> instead of `struct User: GraphQLNamedFragment`
@objc
func fetchUsers(accessToken: String, completion: @escaping (([[String:Any]]?, NSError?) ->())) {
  _ = client.fetchUsers(dematerializeResult(completion)) // Swift-only function returning Users struct
}

// Call `completion` with `User struct` converted to dictionary.
private
func dematerializeResult<T: GraphQLNamedFragment, E: Error>(
  _ completion: @escaping (([[String:Any]]?, NSError?) ->())
) -> ((Result<[T], E>) -> ()) {
  return { (result: Result<[T], E>) in
    switch result {
      case .success(let value): completion(value.flatMap({ $0.dictionary() }), nil)
      case .failure(let error): completion(nil, error as NSError?)
    }
  }
}

This has a few downsides. One of them is that any type-safety and compile-time guarantees are completely lost.

Another one would be also generate Objective-C compatible model classes that can be initialized with the GraphQLNamedFragment structs.

public struct UserDetails: GraphQLNamedFragment {
  // [...]
  public let name: String
  // [...]
}

public class UserDetailsObjc: NSObject {
  public let name: String
  public init(userDetails: UserDetails) {
    name = userDetails.name
  }
}

Let's discuss ๐ŸŽฑ

apollo-codegen throws error

Hello there,

I was about to try the library but got stuck at the schema download step:

apollo-codegen download-schema {server url} --output schema.json

module.js:340
    throw err;
          ^
Error: Cannot find module '.'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/Users/j.gorman/.nvm/v0.10.36/lib/node_modules/apollo-codegen/lib/cli.js:32:9)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)

It failed before that with Error: Cannot find module 'process', I ran npm install -g process which fixed that but this error now isn't as easy to figure out. Any hints?

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

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"))
        }
      }
    }
  }
}

Possibility of using this API in an objective-c environment

Currently I have an objective-c based project and i'd like use it in my project. I know I can't work with swift structs and enums (unless it's strictly of type int). So i'm wondering how would I go about using this API in my objective-c project?

Removing JSON parsing

Would it make sense to remove JSON Parsing at all to concentrate on GraphQL. There are a lot of JSONMappers around and reimplementing them wouldn't make sense.

Think about it. Would like to discuss it ๐Ÿ‘

Pod loading version 0.3.1

I have setup my pod file use Appolo. The latest version 0.5.4, but Cocoapods using my pod file is loading Appolo 0.3.1.

When will the pod be updated with the latest version ?

Allow access to JSON used in deserializing GraphQL structs and GraphQLResultReader subclassing

I'm opening this issue to ask whether the following could be supported:

  • Access to the JSON that an struct was de-serialized with
  • Allowing custom deserialization through potentially being able to subclass GraphQLResultReader

To give some context, I'm attempting to create a data synchronization layer on top of GraphQL queries. At a high level, I'm storing the original JSON, updating that JSON when queries to the server return new values, alerting GraphQL structs that their properties have changed, and recreating the object using the reader and updated JSON. I've hacked something together to achieve this, namely:

  • made GraphQLResultReader initializer public
  • made responseName in Field public
  • made currentObject in GraphQLResultReader public

I think there's probably a better way to do what I want with the library's general purpose in mind, and would like to see if something like this would be able to be supported in an official capacity.

Would either of the above asks be possible to support? If you think that this would be helpful to have, I'm also happy to attempt a PR.

Multi-level arrays are not supported

I have a data structure which contains an array of arrays of Double (e.g. a list of waypoints). When I try to compile I get the following error: "No 'list' candidates produce the expected contextual result type '[[Double]]'". This happens with version 0.4.3.

public struct Waypoints: GraphQLMappable {
          public let coordinates: [[Double]]

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

Handle @include and @skip directives

Apollo-ios should handle @include and @Skip directives

The following example generates an error when the photos field should be skipped (or not included)

query UserQuery($includePhotos: Boolean = true) {
  user {
    ...ProfileFragment
  }
}

fragment ProfileFragment on User {
  id
  photos @include(if: $includePhotos) {
    url
  }
}

In the generated code for the above example photos is a non-optional photos property.

public let photos: [Photo?]

When trying to do this query with includePhotos set to false apollo-ios will fail with a GraphQLResultError (underlying Apollo.JSONDecodingError.missingValue)

I believe the expected behavior in this scenario would be making Photos (and any other field that have a @Skip or @include directive) an Optional type, in this case:

public let photos: [Photo?]?

Incorrect code generation

image

Portion of code:

public final class ViewerUserDataQuery: GraphQLQuery {
  public static let operationDefinition =
    "query viewerUserData($keys: [String!]) {" +
    "  viewer {" +
    "    user {" +
    "      data(keys: $keys) {" +
    "        key" +
    "        value" +
    "      }" +
    "    }" +
    "  }" +
    "}"

  public let keys: [String]?

  public init(keys: [String]? = nil) {
    self.keys = keys
  }

  public var variables: GraphQLMap? {
    return ["keys": keys] // Error here
  }

The autosuggestion corrects the issue, but re building reruns the script...

  public var variables: GraphQLMap? {
    return ["keys": keys as! Optional<JSONEncodable>]
  }

Why iOS only?

Hi,

great work! ๐Ÿ‘

I was wondering why you're limiting this to iOS and not support swift in general (iOS, macOS, potentially Linux)? I could not find any iOS specific code? Would you be open to pull requests targeting general swift support (not only iOS)?

Background: We're currently evaluating existing swift/objc-based graphQL libraries for a macOS deployment target.

Thanks,
Max

Use of unresolved identifier 'undefined'

Hey,

I have this mutation definition:

mutation GetPhotoUploadURL($filename: String!) {
  
  photoGetUploadUrl(input: {
                    filename: $filename
                    content_type: "image/jpg"
                    photo_type: SELFIE
                    source: CAMERA
                    count: 1
                    })
  {
    upload_url
    photo_id
  }
}

And the generated API file appears to have an error:

 public struct Data: GraphQLMappable {
    public let photoGetUploadUrl: PhotoGetUploadUrl?

    public init(reader: GraphQLResultReader) throws {
      photoGetUploadUrl = try reader.optionalValue(for: Field(responseName: "photoGetUploadUrl", arguments: ["input": ["filename": undefined, "content_type": "image/jpg", "photo_type": "SELFIE", "source": "CAMERA", "count": 1]]))
    }

[...]

Use of unresolved identifier 'undefined'

Anything wrong with the way I am declaring my mutation?

Cached responses yield `412 Precondition Failed`

I created an app using Githubs API, and noticed that the default cache policy was preventing all but the first request to succeed.

Failed response:

<NSHTTPURLResponse: 0x610000039940> { URL: https://api.github.com/graphql } { status code: 412, headers {
    "Access-Control-Allow-Origin" = "*";
    "Access-Control-Expose-Headers" = "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval";
    "Cache-Control" = "private, max-age=60, s-maxage=60";
    "Content-Length" = 0;
    "Content-Security-Policy" = "default-src 'none'";
    "Content-Type" = "text/html;charset=utf-8";
    Date = "Thu, 09 Mar 2017 14:27:52 GMT";
    Etag = "\"4853e207b0ae85065437a2f600a2d1aa\"";
    Server = "GitHub.com";
    Status = "412 Precondition Failed";
    "Strict-Transport-Security" = "max-age=31536000; includeSubdomains; preload";
    Vary = "Accept, Authorization, Cookie, X-GitHub-OTP";
    "X-Content-Type-Options" = nosniff;
    "X-Frame-Options" = deny;
    "X-GitHub-Request-Id" = "CF6E:32C5:2B947BF:3797DE8:58C16668";
    "X-RateLimit-Limit" = 200;
    "X-RateLimit-Remaining" = 195;
    "X-RateLimit-Reset" = 1489072275;
    "X-XSS-Protection" = "1; mode=block";
} }

If I set configuration.requestCachePolicy = .reloadIgnoringLocalCacheData the issue goes away

https://github.com/davidmuzi/graphql-ios-presentation/blob/2-queries/Projects/Projects/MasterViewController.swift#L25 (edited)

The httpBody and requestHeaders appear identical before the sessionTask is executed

Weird caching behavior

Hey @martijnwalraven,

we've encountered a weird caching behavior around this query:

query GetLinkedIdentities {
    me {
        id
        linked_identities {
            records {
                id
                name
                relationship
                current_photo {
                    ...PhotoDetails
                }
            }
        }
    }
}

When we run our app, this query is executed first thing before anything else.

  • If link_identities are 0 when the app starts, even if we issue a mutation to add a new linked_identity and re-run the GetLinkedIdentities query, it still returns 0 results.
  • If linked_identities are > 0 when the app starts, when we issue a mutation to add a new linked_identity and re-reun the GetLinkedIdentities query, it fetches back the proper number of results.

Running the query with cachePolicy: .fetchIgnoringCacheData seems to solve the problem.

Does this look normal in terms of how Apollo client is handling client-side caching or could this be a bug?

JSON Scalar throws JSONDecondingError.couldNotConvert

We have a custom scalar type called JSON, which is used when we want to send unstructured data.
When trying to access that field using Apollo (even just trying to print it), we get the following error:

Apollo.JSONDecodingError.couldNotConvert({
    amountChanged = "-1467";
}, Swift.String)))

I'm not sure why it would have a problem converting to a String. Also, how can we get the Dictionary value rather than a String?

This may be somewhat similar to #23. I tried the proposed solution there (just aliasing JSON to [String : Any?]) but it gave a bunch of compile errors.

GraphQL enum type with values that are reserved Swift keywords

That's an interesting case. Our GraphQL backend has an AlbumPrivacies enum with values PRIVATE and PUBLIC.

Reasonably, Apollo tries to generate the respective Swift enum in the following way:

/// The privacy modes for an album.
public enum AlbumPrivacies: String {
  case public = "PUBLIC" /// Public albums can be joined and viewed by anyone.
  case private = "PRIVATE" /// Private albums can be joined and viewed only with an invitation.
}

Unfortunately, public and private are of course reserved Swift keywords so unfortunately the generated code is broken.

I am not sure what's the most appropriate way to mitigate this on your side. Maybe check whether an enum value is a reserved keyword and prefix/suffix something to its name declaration? So in that case it could be publicCase and privateCase or something.

Just thinking out loud here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.