     private func remove(starScream: StarScream) {
         self.openWebSockets.removeAll(where: { $0 === starScream })
     }
+
+    private func createStarScream<R: Request>(request: R, completion: @escaping WebSocketCompletionBlock) throws -> StarScream? {
+        guard let urlRequest = try self.createRequest(request) else {
+            return nil
+        }
+        
+        let dispatchQueue = DispatchQueue(label: request.rootKeyPath, qos: .background)
+        return StarScream(request: urlRequest, dispatchQueue: dispatchQueue) { [weak self] (eventReceiver) in
+            self?.delegate?.didReceive(event: eventReceiver.event)
+            
+            switch eventReceiver.event {
+            case .connected(let headers):
+                completion(.success(.connected(headers)))
+            case let .disconnected(reason, code):
+                completion(.success(.disconnected(reason, code)))
+            case .binary(let data):
+                do {
+                    let decoder = JSONDecoder()
+                    let object = try decoder.decode(R.SerializedObject.self, from: data)
+                    completion(.success(.data(object)))
+                }
+                catch let error {
+                    completion(.failure(error))
+                }
+            case let .error(error):
+                if let error = error {
+                    completion(.failure(error))
+                }
+                
+                self?.remove(starScream: eventReceiver.starScream)
+            case .cancelled:
+                self?.remove(starScream: eventReceiver.starScream)
+            case let .reconnectSuggested(shouldReconnect):
+                if shouldReconnect {
+                    eventReceiver.starScream.reconnect()
+                }
+            case .text,
+                 .ping,
+                 .pong,
+                 .viabilityChanged:
+                break
+            }
+        }
+    }
+    
+    private func createRequest<R: Request>(_ request: R) throws -> URLRequest? {
+        let query = try request.queryDocument.graphQLString()
+        var parameters: [String : Any] = ["query" : query]
+        if let variables = try request.variables?.graphQLVariablesDictionary() {
+            parameters["variables"] = variables
+        }
+        
+        guard let url = URL(string: self.baseUrl) else {
+            return nil
+        }
+        
+        let request = try URLRequest(url: url, method: .post, headers: HTTPHeaders(self.httpHeaders))
+        
+        return try URLEncoding.default.encode(request, with: parameters)
+    }
 }
 
 public class StarScream {
     typealias EventReceiver = (starScream: StarScream, event: WebSocketEvent)
     
-    let webSocket: WebSocket
+    var webSocket: WebSocket?
+    let request: URLRequest
     let eventReceiver: (EventReceiver) -> Void
     
-    init(request: URLRequest, eventReceiver: @escaping ((EventReceiver) -> Void)) {
+    init(request: URLRequest,
+         dispatchQueue: DispatchQueue,
+         eventReceiver: @escaping ((EventReceiver) -> Void)) {
+        self.request = request
         self.webSocket = WebSocket(request: request)
+        self.webSocket?.callbackQueue = dispatchQueue
         self.eventReceiver = eventReceiver
     }
     
     deinit {
-        self.webSocket.disconnect()
+        self.webSocket?.forceDisconnect()
+        self.webSocket = nil
     }
     
     func connect() {
-        self.webSocket.connect()
+        self.webSocket?.connect()
     }
     
     func disconnect() {
-        self.webSocket.disconnect()
+        self.webSocket?.disconnect()
+    }
+    
+    func forceDisconnect() {
+        self.webSocket?.forceDisconnect()
+    }
+    
+    func reconnect() {
+        self.webSocket = WebSocket(request: self.request)
+        self.webSocket?.connect()
     }
 }
 

        case connected([String: String])
        case disconnected(String, UInt16)
        case data(SerializedObject)
        case pong(Data?)
        case ping(Data?)
        case error(Error?)
    }
    
    public let baseUrl: String
    public var httpHeaders: [String : String]
    public internal(set) var openWebSockets = [StarScream]()
    
    public init(baseUrl: String,
                httpHeaders: [String : String] = [:]) {
        self.baseUrl = baseUrl
        self.httpHeaders = httpHeaders
    }
    
    open func subscribe<R: Request>(_ request: R, completion: @escaping WebSocketCompletionBlock) {
        do {
            let query = try request.queryDocument.graphQLString()
            var parameters: [String : Any] = ["query" : query]
            if let variables = try request.variables?.graphQLVariablesDictionary() {
                parameters["variables"] = variables
            }
            
            guard let url = URL(string: self.baseUrl) else {
                return
            }
            
            let request = try URLRequest(url: url, method: .post)
            let starScream = StarScream(request: request) { (eventReceiver) in
                DispatchQueue.main.async {
                    switch eventReceiver.event {
                    case .connected(let headers):
                        completion(.success(.connected(headers)))
                    case let .disconnected(reason, code):
                        completion(.success(.disconnected(reason, code)))
                    case .binary(let data):
                        do {
                            let decoder = JSONDecoder()
                            let object = try decoder.decode(R.SerializedObject.self, from: data)
                            completion(.success(.data(object)))
                        }
                        catch let error {
                            completion(.failure(error))
                        }
                    case let .ping(data):
                        completion(.success(.ping(data)))
                    case let .pong(data):
                        completion(.success(.pong(data)))
                    case let .error(error):
                        if let error = error {
                            completion(.failure(error))
                        }
                        
                        self.remove(starScream: eventReceiver.starScream)
                    case .cancelled:
                        self.remove(starScream: eventReceiver.starScream)
                    case .text,
                         .viabilityChanged,
                         .reconnectSuggested:
                        break
                    }
                }
            }
            
            self.openWebSockets.append(starScream)

import Foundation
import Starscream
import Alamofire

public typealias WebSocketCompletionBlock = (Result<WebSocketClient.Events, Error>) -> Void

public protocol WebSocketClientDelegate {
    func didReceive(event: WebSocketEvent)
}

open class WebSocketClient {
    public typealias SerializedObject = Decodable
