apollographql / apollo-ios

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

Apollo fetch function does not work #909

Closed nandofer1 closed 4 years ago

nandofer1 commented 4 years ago

I am starting to use apollo-ios with the github API, I have followed the steps in this link ( https://www.back4app.com/docs/ios/swift-graphql), I have generated the scheme.json file with my github authorization token and also created my .graphql file with the query that I am sure it works because I tested in the graphQL console application but when implementing the code and doing the fetch does not show me anything, it does not print or error or the parameter I want to print.

Here is my code snippet

    let apollo: ApolloClient = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = [
            "Authorization": "Bearer <my git token>"
        ]

        let url = URL(string: "https://api.github.com/graphql")!

        return ApolloClient(
            networkTransport: HTTPNetworkTransport(
                url: url
            )
        )
    }()

    apollo.fetch(query: SearchUsersQuery(user: "nandofer1")){
        result in
        guard let data = try? result.get().data else { return }
        print(data.search.__typename)
    }

and here is what my .graphQL file contains

query searchUsers($user: String!) { search(query: $user, type: USER, first: 100) { nodes { __typename ... on User { name login avatarUrl url bio } } } }

Will I be missing something? please help.

designatednerd commented 4 years ago

Hey @nandofer1 - are you getting an error from the GitHub API, or is the print statement just printing nothing? It might be helpful to switch on the result so you can see what's in the success vs. failure case, and which one is being hit.

nandofer1 commented 4 years ago

@designatednerd - the print statement just printing nothing in the console . I don't receive any warning or error, I don't know if he's doing the fetch, I've tried this way with a switch to handle the different cases :

apollo.fetch(query: SearchUsersQuery(user: "nandofer1")) { result in switch result { case .success(let graphQLResult): if let data = graphQLResult.data { print(data) } else if let errors = graphQLResult.errors { print(errors) } case .failure(let error): // Network or response format errors print(error) } }

but he still doesn't print anything when executing the fetch.

designatednerd commented 4 years ago

One thing to watch out for: Is there anything holding on to your apollo client instance, like a singleton or a view controller? If there isn't anything, that completion block won't get called because the client got deallocated.

nandofer1 commented 4 years ago

@designatednerd
the code snippet is called in the viewdidload of my view controller in this way:

import Apollo import Foundation class SearchsUsersViewController: UIViewController{

override func viewDidLoad() {
    super.viewDidLoad()
    print("get in viewdidload")
    let apollo: ApolloClient = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = [
            "Authorization": "Bearer <my token github>"
        ]

        let url = URL(string: "https://api.github.com/graphql")!

        return ApolloClient(
            networkTransport: HTTPNetworkTransport(
                url: url
            )
        )
    }()

    /*apollo.fetch(query: SearchUsersQuery(user: "nandofer1")){
        result in
        guard let data = try? result.get().data else { return }
        print(data.search.__typename)
    }*/

    apollo.fetch(query: SearchUsersQuery(user: "nandofer1")) { result in
      switch result {
      case .success(let graphQLResult):
        if let data = graphQLResult.data {
          print(data)
        } else if let errors = graphQLResult.errors {
          print(errors)
        }
      case .failure(let error):
        // Network or response format errors
        print(error)
      }
    }
}

}

designatednerd commented 4 years ago

So that let should be outside the viewDidLoad, which will make it a property. This is because otherwise as soon as the scope of viewDidLoad exits, Automatic Reference Counting will go "Oh, nobody's using this anymore, I can kill this off", and deallocate any variable declared within the viewDidLoad().

TL;DR:

let apollo: ApolloClient = {
  [existing code] 
}()

override func viewDidLoad() {
    super.viewDidLoad()
    print("get in viewdidload")

    apollo.fetch(query: SearchUsersQuery(user: "nandofer1")) { result in
    ...
nandofer1 commented 4 years ago

Is right, You must be out of viewDidLoad, now if you make the request and I am getting an error:

GraphQLHTTPResponseError(body: Optional(131 bytes), response: <NSHTTPURLResponse: 0x600003aaf240> { URL: https://api.github.com/graphql } { Status Code: 401, Headers { "Access-Control-Allow-Origin" = ( "*" ); "Access-Control-Expose-Headers" = ( "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type" ); "Content-Length" = ( 131 ); "Content-Security-Policy" = ( "default-src 'none'" ); "Content-Type" = ( "application/json; charset=utf-8" ); Date = ( "Mon, 18 Nov 2019 14:52:08 GMT" ); "Referrer-Policy" = ( "origin-when-cross-origin, strict-origin-when-cross-origin" ); Server = ( "GitHub.com" ); Status = ( "401 Unauthorized" ); "Strict-Transport-Security" = ( "max-age=31536000; includeSubdomains; preload" ); "X-Content-Type-Options" = ( nosniff ); "X-Frame-Options" = ( deny ); "X-GitHub-Media-Type" = ( "github.v4; format=json" ); "X-GitHub-Request-Id" = ( "ABAD:3026:60CBDD:CED40D:5DD2B018" ); "X-RateLimit-Limit" = ( 0 ); "X-RateLimit-Remaining" = ( 0 ); "X-RateLimit-Reset" = ( 1574092328 ); "X-XSS-Protection" = ( "1; mode=block" ); } }, kind: Apollo.GraphQLHTTPResponseError.ErrorKind.errorResponse, serializationFormat: Apollo.JSONSerializationFormat)

in the header of apollo I have put the key and the value of the token that I have generated in Github, I have tried it with the same token in the GraphQL console and it works.

designatednerd commented 4 years ago

Aha, I think I see it: You're setting up the configuration object but you're never passing it to the HTTPNetworkTransport object, so the network transport has no idea it exists. I'm surprised you're not getting an unused variable warning there.

You'll need to hand the configuration object to a URLSession object, and then hand that to the HTTPNetworkTransport object. Then the network transport will know to use your configuration and add the proper header.

nandofer1 commented 4 years ago

I've been trying to add the configuration parameter to the HTTPNetworkTransport like this:

let apollo: ApolloClient = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = [ "Authorization": "Bearer " ]

    let url = URL(string: "https://api.github.com/graphql")!

    return ApolloClient(
        networkTransport: HTTPNetworkTransport(
            url: url,
            configuration: configuration

        )
    )
}()

but it shows me an error as well as you tell me that you are surprised that you do not receive a warning that a parameter is needed on the contrary when sending the configuration the parameter received the error extra parameter in call.

I have been seeing other forms and about a year ago if the configuration was sent as a parameter.

designatednerd commented 4 years ago

This is a (relatively) recent change - to allow for greater flexibility, we updated the initializer to take the URL session rather than just the configuration.

Current documentation for the class is available either inline or on our documentation website.

nandofer1 commented 4 years ago

@designatednerd
It worked with the URL session parameter, thank you very much for everything! (y)

designatednerd commented 4 years ago

@nandofer1 - mind if we close this out? I can't tell if you reopened accidentally or on purpose 🙃

nandofer1 commented 4 years ago

It was by accident, we can close it. thank you! :D

gbrigens commented 4 years ago

Am running to this error Cannot find 'HTTPNetworkTransport' in scope and Cannot find type 'HTTPNetworkTransportDelegate' in scope when am trying to make calls, ApolloClient Version 0.34.1

Here is my code;

final class Network {
    static let shared = Network()
    private lazy var networkTransport: NetworkTransport = {

        let transport = HTTPNetworkTransport(url: URL(string: "https://exampe.com/grapghql")!)
        transport.delegate = self

        return transport
    }()

    private(set) lazy var apollo = ApolloClient(networkTransport: self.networkTransport)
}

extension Network: HTTPNetworkTransportDelegate {
    func networkTransport(_ networkTransport: NetworkTransport, shouldSend request: URLRequest) -> Bool {
        return true
    }

    func networkTransport(_ networkTransport: NetworkTransport, willSend request: inout URLRequest) {

        let token = ""
        var headers = request.allHTTPHeaderFields ?? [String: String]()
        headers["Authorization"] = "Bearer \(token)"

        request.allHTTPHeaderFields = headers
    }
}

I appreciate your feedback.