Using Apollo with AppSync directly on iOS

For the past few days I have been trying to get the apollo-ios subscriptions to work with AppSync with no luck. I am able to fetch the schema, generate the API.swift file, and run querys but when I try to setup the WebSocketTransport no matter what I pass I am getting

    errorType = UnsupportedOperation;
    message = "unknown not supported through the realtime channel";

I am setting up the Apollo Client like this:

class Network {
    static let shared = Network()
    private lazy var cache = InMemoryNormalizedCache()
    private lazy var store = ApolloStore(cache: cache)

    private lazy var webSocketTransport: WebSocketTransport = {

        let authDict = [
            "x-api-key": "API_KEY",
            "host": "<API_URL>"

        let headerData: Data = try! authDict, options: JSONSerialization.WritingOptions.prettyPrinted)
        let headerBase64 = headerData.base64EncodedString()

        let payloadData = try! [:], options: JSONSerialization.WritingOptions.prettyPrinted)
        let payloadBase64 = payloadData.base64EncodedString()

        let authPayload: [String : JSONEncodable] = [
            "header": headerBase64,
            "payload": payloadBase64

        let url = URL(string: "wss://<API_URL>\(headerBase64)&payload=\(payloadBase64)")!
        let request = URLRequest(url: url)

        return WebSocketTransport(request: request, sendOperationIdentifiers: false, reconnectionInterval: 5)

    /// An HTTP transport to use for queries and mutations
    private lazy var normalTransport: RequestChainNetworkTransport = {
        let url = URL(string: "https://<API_URL>")!
        return RequestChainNetworkTransport(interceptorProvider: LegacyInterceptorProvider(store: store), endpointURL: url, additionalHeaders: ["X-Api-Key": "API_KEY"])

    /// A split network transport to allow the use of both of the above
    /// transports through a single `NetworkTransport` instance.
    private lazy var splitNetworkTransport = SplitNetworkTransport(
        uploadingNetworkTransport: self.normalTransport,
        webSocketNetworkTransport: self.webSocketTransport

    private(set) lazy var apollo: ApolloClient = {
        return ApolloClient(networkTransport: splitNetworkTransport, store: store)

and when setting up a subscription I did:

class ViewController: UIViewController {
    var subscription: Cancellable?

    override func viewDidLoad() {
        subscription = Network.shared.apollo.subscribe(subscription: SubscribeCommentsSubscription()) { result in
            switch result {
            case .success(let graphQLResult):
              print("Success! Result: \(graphQLResult)")
            case .failure(let error):
              print("Failure! Error: \(error)")

    deinit {
      // Make sure the subscription is cancelled, if it exists, when this object is deallocated.

Just to share a snippet from API.swift

    subscription subscribeComments {
      subscribeToEventComments(eventId: "EVENT_ID") {

A similar setup is working on Android so I am at a loss for what is not working correctly…

In Addition to the above I tried creating my own body for the Subscription Request to no avail…

class AppSyncRequestBodyCreator: RequestBodyCreator {
    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"] = UUID().uuidString

        if sendQueryDocument {
            let data = try! ["query": operation.queryDocument,
                                                                    "variables": [:]], options: .prettyPrinted)
            let jsonString = String(data: data, encoding: .utf8)
            body["data"] = jsonString

        let authDict = [
            "x-api-key": "API_KEY",
            "host": "",
        body["extension"] = ["authorization": authDict]

        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]

        return body

I do not have a solution but am experiencing the same problem on Mac with Node.js (v16) - my code is described in Using `client.subscribe` does not work? (to AppSync, from Node) When I add logs to the ws implementation, I see:

ws.msg {"type":"connection_ack","payload":{"connectionTimeoutMs":300000}}
ws.msg {"type":"ka"}
ws.msg {"type":"error","payload":{"errors":[{"errorType":"UnsupportedOperation","message":"unknown not supported through the realtime channel"}]}}

I suppose I got to the “connection init ack” as described in the docs. The error then I guess comes from AppSync. However the Python client - also referenced from my post - works just fine. There the messages are:

### opened ###
>> {"type": "connection_init"}
### message ###
<< {"type":"connection_ack","payload":{"connectionTimeoutMs":300000}}
>> {"id": "04acda54-438d-4558-8472-211b76df0f73", "payload": {"data": "{\"query\": \"subscription JhTestSubscription {onOrderAndRequestOwnerConsent(id: \\\"281c6d08-146b-4b05-aeb9-ef12c5ed1cc5\\\") {id,orderId,state}}\", \"variables\": {}}", "extensions": {"authorization": {"host": "<secret>", "x-api-key": "da2-<secret>"}}}, "type": "start"}

The changes described here Document how to use client with AppSync · Issue #224 · apollographql/apollo-feature-requests · GitHub solved the problem for me. You would need to do something similar in the iOS code, I imagine. No idea why a similar setup works on Android.

(Edited by admin @AnthonyMDev to fix broken link)