I just came across this post and solved this issue so I will share some code for others that run into it. Took me a while of trial and error to piece it together. I am integrating with AppSync here via api-key auth. In the snippet below you can see the additional header I add.
To get it to work, create the transport / client as normal, but what is required is to setup a custom request body creator as mentioned above. You have to pass it to any transport you create. In my case this is for a AppSync websocket endpoint.
// Create the websocket client
let webSocketClient = WebSocket(request: request, protocol: .graphql_ws)
// This custom body wrapper is required for AppSync's protocol
let requestBody = AppSyncRequestBodyCreator()
requestBody.authorization = authHeaders
let webSocketTransport = WebSocketTransport(websocket: webSocketClient, requestBodyCreator: requestBody)
let normalTransport = RequestChainNetworkTransport(interceptorProvider: DefaultInterceptorProvider(store: store), endpointURL: normalUrl, additionalHeaders: ["x-api-key" : parsedResponse.data.config.appSyncKey])
// Using a split transport allows us to handle both standard HTTP queries and websockets through the same client
let splitTransport = SplitNetworkTransport(uploadingNetworkTransport: normalTransport, webSocketNetworkTransport: webSocketTransport)
self.client = ApolloClient(networkTransport: splitTransport, store: self.store)
The code for the RequestBodyCreator:
/**
Creates an AWS AppSync-compatible RequestBody for subscriptions.
Credit to: https://community.apollographql.com/t/using-apollo-with-appsync-directly-on-ios/324/4
*/
public class AppSyncRequestBodyCreator : RequestBodyCreator {
// Don't forget to to set these! This is required for the initial handshake.
var authorization: [String : String]!
public func requestBody<Operation>(for operation: Operation, sendOperationIdentifiers: Bool, sendQueryDocument: Bool, autoPersistQuery: Bool) -> GraphQLMap where Operation : GraphQLOperation {
var body: GraphQLMap = [
"variables": operation.variables,
"operationName": operation.operationName,
]
if sendOperationIdentifiers {
guard let operationIdentifier = operation.operationIdentifier else {
preconditionFailure("To send operation identifiers, Apollo types must be generated with operationIdentifiers")
}
body["id"] = operationIdentifier
}
if sendQueryDocument {
body["query"] = operation.queryDocument
}
// The data portion of the body needs to have the query and variables as well.
guard let data = try? JSONSerialization.data(withJSONObject: ["query": operation.queryDocument,
"variables": operation.variables ?? [:]], options: .prettyPrinted) else {
fatalError("Somehow the query and variables aren't valid JSON!")
}
let jsonString = String(data: data, encoding: .utf8)
body["data"] = jsonString
if autoPersistQuery {
guard let operationIdentifier = operation.operationIdentifier else {
preconditionFailure("To enable `autoPersistQueries`, Apollo types must be generated with operationIdentifiers")
}
body["extensions"] = [
"persistedQuery" : ["sha256Hash": operationIdentifier, "version": 1],
"authorization": authorization
]
} else {
body["extensions"] = ["authorization": authorization]
}
return body
}
}