Search code examples
iosswiftgraphqlapolloapollo-ios

How to correctly use Apollo GraphQL on iOS with background session configuration?


I use Apollo iOS 0.8 with Xcode 9.3, Swift 4.1 and iOS 11, and initialise Apollo client instance like this:

import Apollo

// ... unrelated code skipped

let configuration = URLSessionConfiguration.default

if let token = keychain.accessToken {
  // Add additional headers as needed
  configuration.httpAdditionalHeaders = [
    "Authorization": "Bearer \(token)"
  ]
}

let graphqlEndpoint = URL("https://sample-server-url/graphql")!
let client = ApolloClient(networkTransport:
  HTTPNetworkTransport(url: graphqlEndpoint, configuration: configuration))

The application works well with all queries and mutations sent to the GraphQL server without a problem, except when the app is in background. As far as I know, with common NSURLSession instance it can be easily solved by switching session configuration to URLSessionConfiguration.background(withIdentifier: "your-session-id").

But when I replace the line

let configuration = URLSessionConfiguration.default

with

let configuration = URLSessionConfiguration.background(withIdentifier: "your-session-id")

the app starts crashing with this error: Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'

What's the best way to resolve this error when using Apollo GraphQL or is there any other way to communicate with a GraphQL server in background?


Solution

  • Apollo iOS provides a public NetworkTransport protocol that allows overriding all networking interactions. An implementation can be provided as an argument to ApolloClient(networkTransport: NetworkTransport) initialiser.

    Suppose you have a NetworkTransport implementation that wraps URLSession implementation with background session configuration:

    class BackgroundTransport: NetworkTransport {
        public func send<Operation>(operation: Operation,
        completionHandler: @escaping (GraphQLResponse<Operation>?, Error?) -> Void)
        -> Cancellable where Operation: GraphQLOperation {
        // ...
        }
    }
    

    Then you can initialise ApolloClient this way:

    let graphqlEndpoint = URL("https://sample-server-url/graphql")!
    let client = ApolloClient(networkTransport: BackgroundTransport(url: u))
    

    BackgroundTransport implementation can be as custom as needed, including using URLSession delegate instead of completion handler blocks, as required for background session configuration.

    If you use Alamofire in your app, you can also use ApolloAlamofire library that provides an implementation of NetworkTransport with support for background URLSession configuration and a few more features.