Uploading of files no longer working after setting up Split Transport to support Web Sockets / Subscriptions

Recently change the Apollo client files to include the Split Transport method to support Subscriptions with Web Sockets. After doing this, our upload request is failing and after hours of digging, I can’t seem to figure out what broke or what I need to add. Here’s the code and error…

Previous Apollo Client:

class ApolloManager {

    static let shared = ApolloManager()
    lazy var apollo: ApolloClient? = initializeGQLClient()

    // This should only be called ONCE. Before, we initialized a new client for every single GQL call. This client is initiazlied once and used throughout
    func initializeGQLClient() -> ApolloClient {
        var graphQLEndpoint: URL!
        if Env.isDev {
            graphQLEndpoint = URL(string: "[URL]")
        } else {
            graphQLEndpoint = URL(string: "[URL]")
        }
        print("YASHIE LOOK HERE ---- \(graphQLEndpoint)")
        let nt = HTTPNetworkTransport(url: graphQLEndpoint)
        nt.delegate = self
        return ApolloClient(networkTransport: nt, store: ApolloStore(cache: InMemoryNormalizedCache()))
    }
}

extension ApolloManager: HTTPNetworkTransportPreflightDelegate {
    func networkTransport(_ networkTransport: HTTPNetworkTransport, shouldSend request: URLRequest) -> Bool {
        return true
    }

    // This delegate function allows us to add the authorization header with each GQL request that contains the JWT that is verified by frontend-srv
    func networkTransport(_ networkTransport: HTTPNetworkTransport, willSend request: inout URLRequest) {
        var headers = request.allHTTPHeaderFields ?? [String: String]()
        headers["Authorization"] = "Bearer \(UserManager().getUserJwt())"
        print("HERE IS THE JWT FOR AUTH --- \(UserManager().getUserJwt())")
        request.allHTTPHeaderFields = headers
    }
}

New Apollo Client

class ApolloManager {
    
    static let shared = ApolloManager()
    
    lazy var apollo: ApolloClient? = initializeGQLClient()
  
    func initializeGQLClient() -> ApolloClient {
          // The cache is necessary to set up the store, which we're going to hand to the provider
        let cache = InMemoryNormalizedCache()
        let store = ApolloStore(cache: cache)

        let client = URLSessionClient()
        
        let provider = NetworkInterceptorProvider(client: client, store: store)
          
        var url: URL!
        
        if Env.isDev {
            url = URL(string: "https://[URL]")
        } else {
            url = URL(string: "https://[URL]")
        }

        let requestChainTransport = RequestChainNetworkTransport(interceptorProvider: provider,
                                                               endpointURL: url)
                           
        // for websocket
        var wsurl: URL!
        
        if Env.isDev {
            wsurl = URL(string: "ws://[URL]")
        } else {
            wsurl = URL(string: "ws://[URL]")
        }
        
        let wsrequest = URLRequest(url: wsurl)
        
        let token = "Bearer \(UserManager().getUserJwt())"
        
        let authPayload = ["Authorization": token]
        
        let webSocketTransport = WebSocketTransport(request: wsrequest, reconnect: true, connectingPayload: authPayload)
        
        var splitNetworkTransport = SplitNetworkTransport(
          uploadingNetworkTransport: requestChainTransport,
          webSocketNetworkTransport: webSocketTransport
        )
        
        // Remember to give the store you already created to the client so it
        // doesn't create one on its own
//        return ApolloClient(networkTransport: requestChainTransport,
//                          store: store)
        return ApolloClient(networkTransport: splitNetworkTransport,
                          store: store)
    }
}

class TokenAddingInterceptor: ApolloInterceptor {
    func interceptAsync<Operation: GraphQLOperation>(
        chain: RequestChain,
        request: HTTPRequest<Operation>,
        response: HTTPResponse<Operation>?,
        completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void) {
        
        let token = "Bearer \(UserManager().getUserJwt())"
        
        request.addHeader(name: "Authorization", value: token)
                
        chain.proceedAsync(request: request,
                           response: response,
                           completion: completion)
    }
}

class NetworkInterceptorProvider: LegacyInterceptorProvider {
    override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
        var interceptors = super.interceptors(for: operation)
        interceptors.insert(TokenAddingInterceptor(), at: 0)
        return interceptors
    }
}

The Upload request that is failing (The Upload only fails when uploading a file – uploading profile picture JPEGs)

func updateUser() -> Promise<String?> {
        return Promise<String?> { seal in
            guard let image = self.user.uploadImageData else {
                print("THERE'S NO IMAGE DATA TO UPLOAD")
                return
            }
            let imageUUID = UUID().uuidString
            let picToUpload = GraphQLFile(fieldName: "picture", originalName: "\(self.user.userInfo.name)-\(imageUUID).png", mimeType: "image/jpeg", data: image)
            let updateMutation = UpdateUserMutation(username: self.user.userInfo.userName, name: self.user.userInfo.name, picture: "picture")
            apollo.upload(operation: updateMutation, files: [picToUpload]) { result in
                switch result {
                case .failure(let error):
                    print("ERROR UPLOADING IMAGE/UPDATE USER ---- \(error)")
                case .success(let result):
                    print("HERE IS THE RESULT FROM IMAGE UPLOAD--- \(result)")
                    if let errors = result.errors, !errors.isEmpty {
                    }
                    let data = result.data?.updateUser
                    print("HERE IS THE DATA FROM UPDATE USER REQUEST ---- \(data)")
                    UserManager().setUserInformationInUD(name: data?.name, un: data?.username, pic: data?.picture, offlineAt: data?.offlineAt)
                    // If it's a brand new user, this would be an ideal location for us to track them in segment
                    // right after they upload their picture
                    if self.user.auth.isNewUser {
                        Analytics.shared().identify(data?.id,
                                                    traits: ["username": data?.username, "name": data?.name,
                                                             "phone": data?.phone])
                        self.user.auth.isNewUser = false
                    }
                    // UPLOAD HAS COMPLETED, THIS WILL MOVE THE VIEW
                    self.user.userInfo.isUploading = false
                    self.user.userInfo.isUploadComplete = true
                    seal.fulfill(data?.picture)
                }
            }
        }
    }

The error that I’m seeing in the console

2021-06-04 17:40:53.112272-0400 •• ɑ[12470:3100824] Task <EEE0B8A5-432F-4B73-B08E-FF054CABCDD1>.<12> HTTP load failed, 1049317/0 bytes (error code: -1005 [4:-4])
2021-06-04 17:40:53.114343-0400 •• ɑ[12470:3100824] [tcp] tcp_input [C2.1:3] flags=[R] seq=660736513, ack=4294967196, win=0 state=CLOSE_WAIT rcv_nxt=660736538, snd_una=213009868
2021-06-04 17:40:53.114765-0400 •• ɑ[12470:3100659] Task <EEE0B8A5-432F-4B73-B08E-FF054CABCDD1>.<12> finished with error [-1005] Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorCodeKey=-4, NSUnderlyingError=0x282979aa0 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={NSErrorPeerAddressKey=<CFData 0x280508370 [0x1ffd5c660]>{length = 16, capacity = 16, bytes = 0x100201bb36ddfb940000000000000000}, _kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <EEE0B8A5-432F-4B73-B08E-FF054CABCDD1>.<12>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <EEE0B8A5-432F-4B73-B08E-FF054CABCDD1>.<12>"
), NSLocalizedDescription=The network connection was lost., NSErrorFailingURLStringKey=https://dev.dotdot.app/graphql, NSErrorFailingURLKey=https://dev.dotdot.app/graphql, _kCFStreamErrorDomainKey=4}
ERROR UPLOADING IMAGE/UPDATE USER ---- networkError(data: 0 bytes, response: nil, underlying: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorCodeKey=-4, NSUnderlyingError=0x282979aa0 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={NSErrorPeerAddressKey=<CFData 0x280508370 [0x1ffd5c660]>{length = 16, capacity = 16, bytes = 0x100201bb36ddfb940000000000000000}, _kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <EEE0B8A5-432F-4B73-B08E-FF054CABCDD1>.<12>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <EEE0B8A5-432F-4B73-B08E-FF054CABCDD1>.<12>"
), NSLocalizedDescription=The network connection was lost., NSErrorFailingURLStringKey=https://dev.dotdot.app/graphql, NSErrorFailingURLKey=https://dev.dotdot.app/graphql, _kCFStreamErrorDomainKey=4})

Any help would be super appreciated!

Did you ever resolve this issue?
We are running into similar problems losing network connections