diff --git a/KoreBotSDK.podspec b/KoreBotSDK.podspec old mode 100644 new mode 100755 index 47e2070a..26e5d0cf --- a/KoreBotSDK.podspec +++ b/KoreBotSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KoreBotSDK' - s.version = '0.1.0' + s.version = '0.1.1' s.license = {:type => 'MIT', :file => 'KoreBotSDK/LICENSE' } s.summary = 'KoreBotSDK lets a user interact with Kore bots' s.homepage = 'https://kore.ai' @@ -26,7 +26,7 @@ Pod::Spec.new do |s| ss.dependency 'Mantle', '2.0.2' ss.dependency 'AFNetworking', '3.2.0' - ss.dependency 'SocketRocket' + ss.dependency 'Starscream', '~> 3.0.2' ss.ios.frameworks = 'SystemConfiguration' end diff --git a/KoreBotSDK/KoreBotSDKDemo/KoreBotSDKDemo.xcodeproj/project.pbxproj b/KoreBotSDK/KoreBotSDKDemo/KoreBotSDKDemo.xcodeproj/project.pbxproj index aab4815c..bec5b3b1 100644 --- a/KoreBotSDK/KoreBotSDKDemo/KoreBotSDKDemo.xcodeproj/project.pbxproj +++ b/KoreBotSDK/KoreBotSDKDemo/KoreBotSDKDemo.xcodeproj/project.pbxproj @@ -718,8 +718,8 @@ "${BUILT_PRODUCTS_DIR}/KoreBotSDK/KoreBotSDK.framework", "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", - "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework", "${BUILT_PRODUCTS_DIR}/SquareMosaicLayout/SquareMosaicLayout.framework", + "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework", "${BUILT_PRODUCTS_DIR}/TOWebViewController/TOWebViewController.framework", "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", @@ -738,8 +738,8 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KoreBotSDK.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SquareMosaicLayout.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TOWebViewController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", diff --git a/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/BotClient.swift b/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/BotClient.swift old mode 100644 new mode 100755 index c50dba64..d1e81060 --- a/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/BotClient.swift +++ b/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/BotClient.swift @@ -7,7 +7,7 @@ // import UIKit -import SocketRocket +import Starscream public enum BotClientConnectionState : Int { case NONE @@ -242,8 +242,8 @@ open class BotClient: NSObject, RTMPersistentConnectionDelegate { // MARK: functions fileprivate func rtmConnectionWithBotInfoModel(_ botInfo: BotInfoModel, isReconnect: Bool) -> RTMPersistentConnection { - if (self.connection != nil && (self.connection.websocket.readyState == .OPEN || self.connection.websocket.readyState == .CONNECTING)) { - return self.connection + if connection != nil { + return connection } else { let rtmConnection: RTMPersistentConnection = RTMPersistentConnection(botInfo: botInfo, botInfoParameters: self.botInfoParameters, tryReconnect: isReconnect) rtmConnection.connectionDelegate = self @@ -253,16 +253,13 @@ open class BotClient: NSObject, RTMPersistentConnectionDelegate { } open func sendMessage(_ message: String!, options: AnyObject!) { - if (self.connection == nil) { + guard let socket = connection.socket else { NSLog("WebSocket connection not available") - } else { - switch self.connection.websocket.readyState { - case .OPEN: - self.connection.sendMessageModel(message, options: [] as AnyObject?) - break - default: - break - } + return + } + + if socket.isConnected { + connection.sendMessageModel(message, options: [] as AnyObject?) } } diff --git a/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/RTM/RTMPersistentConnection.swift b/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/RTM/RTMPersistentConnection.swift old mode 100644 new mode 100755 index c407a69b..90dcdc50 --- a/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/RTM/RTMPersistentConnection.swift +++ b/KoreBotSDK/Library/KoreBotSDK/KoreBotSDK/Library/RTM/RTMPersistentConnection.swift @@ -7,7 +7,7 @@ // import UIKit -import SocketRocket +import Starscream import Mantle @objc public protocol RTMPersistentConnectionDelegate { @@ -19,10 +19,11 @@ import Mantle @objc optional func didReceiveMessageAck(_ ack: Ack) } -open class RTMPersistentConnection : NSObject, SRWebSocketDelegate { +open class RTMPersistentConnection : NSObject, WebSocketDelegate, WebSocketPongDelegate { + var botInfo: BotInfoModel! fileprivate var botInfoParameters: NSDictionary! = nil - var websocket: SRWebSocket! = nil + private(set) public var socket: WebSocket? var connectionDelegate: RTMPersistentConnectionDelegate? fileprivate let timerSource: DispatchSourceTimer @@ -45,95 +46,118 @@ open class RTMPersistentConnection : NSObject, SRWebSocketDelegate { if (self.tryReconnect == true) { url.append("&isReconnect=true") } - self.websocket = SRWebSocket(urlRequest: URLRequest(url: URL(string: url)! as URL) as URLRequest!) - self.websocket.delegate = self - self.websocket.open() + + socket = WebSocket(url: URL(string: url)!) + socket?.delegate = self + socket?.pongDelegate = self + socket?.connect() } open func disconnect() { - if (self.websocket != nil) { - self.websocket.close() + if let socket = socket { + socket.disconnect() } } // MARK: WebSocketDelegate methods - open func webSocketDidOpen(_ webSocket: SRWebSocket!) { + public func websocketDidConnect(socket: WebSocketClient) { self.connectionDelegate?.rtmConnectionDidOpen() let intervalInNSec = pingInterval * Double(NSEC_PER_SEC) let startTime = DispatchTime.now() + Double(intervalInNSec) / Double(NSEC_PER_SEC) timerSource.scheduleRepeating(deadline: startTime, interval: pingInterval, leeway: .nanoseconds(Int(NSEC_PER_SEC / 10))) timerSource.setEventHandler { [unowned self] in + + guard let socket = self.socket else { + return + } if self.receivedLastPong == false { // we did not receive the last pong // abort the socket so that we can spin up a new connection - self.websocket.close() - } else if self.websocket.readyState == .CLOSED || self.websocket.readyState == .CLOSING { - self.websocket.close() + socket.disconnect() + } else if !socket.isConnected { + socket.disconnect() } else { // we got a pong recently // send another ping self.receivedLastPong = false - try? self.websocket.sendPing(nil) + socket.write(ping: Data()) } } timerSource.resume() } - open func webSocket(_ webSocket: SRWebSocket!, didFailWithError error: NSError!) { + public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { + if let error = error as NSError? { self.connectionDelegate?.rtmConnectionDidFailWithError(error) + } + else { + self.connectionDelegate?.rtmConnectionDidClose(0, reason: "unknown") + } + } - open func webSocket(_ webSocket: SRWebSocket, didCloseWithCode code: Int, reason: String, wasClean: Bool) { - self.connectionDelegate?.rtmConnectionDidClose(code, reason: reason as String) + public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { + processMessage(text) } - open func webSocket(_ webSocket: SRWebSocket!, didReceivePong pongPayload: Data!) { + public func websocketDidReceiveData(socket: WebSocketClient, data: Data) { + if let text = String(data: data, encoding: .utf8) { + processMessage(text) + } + } + + public func websocketDidReceivePong(socket: WebSocketClient, data: Data?) { self.receivedLastPong = true } - open func webSocket(_ webSocket: SRWebSocket!, didReceiveMessage message: Any!) { - let responseObject = self.convertStringToDictionary(message as! String)! - if (responseObject["type"]! as! String == "ready") { + + private func processMessage (_ text: String) { + + guard let responseObject = self.convertStringToDictionary(text) else { + return + } + + guard let type = responseObject["type"] as? String else { + return + } + + if (type == "ready") { self.connectionDelegate?.self.rtmConnectionReady() - } else if (responseObject["ok"] != nil) { + } + else if (responseObject["ok"] != nil) { let ack: Ack = try! (MTLJSONAdapter.model(of: Ack.self, fromJSONDictionary: responseObject ) as! Ack) self.connectionDelegate?.didReceiveMessageAck!(ack) - } else if (responseObject["type"]! as! String == "bot_response") { + } + else if (type == "bot_response") { print("received: \(responseObject)") - let array: NSArray = responseObject["message"] as! NSArray - if (array.count > 0) { + + if let message = responseObject["message"] as? NSArray { + if (message.count > 0) { let botMessageModel: BotMessageModel = try! (MTLJSONAdapter.model(of: BotMessageModel.self, fromJSONDictionary: responseObject ) as! BotMessageModel) self.connectionDelegate?.didReceiveMessage!(botMessageModel) } } } - - open func webSocketShouldConvertTextFrameToString() -> ObjCBool { - return true } // MARK: sending message open func sendMessageModel(_ message: String, options: AnyObject?) { - switch (self.websocket.readyState) { - case .CONNECTING: - print("Socket is in CONNECTING state") - break - case .CLOSED: - self.start() - print("Socket is in CLOSED state") - break - case .CLOSING: - print("Socket is in CLOSING state") - break - case .OPEN: - print("Socket is in OPEN state") + + guard let socket = socket else { + print("Socket is not initialised") + start() + return + } + + if socket.isConnected { + print("Socket is connected") let parameters: NSMutableDictionary = NSMutableDictionary() let messageObject = ["body":message, "attachments":[]] as [String : Any]; parameters.setObject(messageObject, forKey: "message" as NSCopying) parameters.setObject("/bot.message", forKey: "resourceid" as NSCopying) - if (self.botInfoParameters != nil) { - parameters.setObject(self.botInfoParameters, forKey: "botInfo" as NSCopying) + if (botInfoParameters != nil) { + parameters.setObject(botInfoParameters, forKey: "botInfo" as NSCopying) } let uuid: String = Constants.getUUID() parameters.setObject(uuid, forKey: "id" as NSCopying) @@ -142,9 +166,13 @@ open class RTMPersistentConnection : NSObject, SRWebSocketDelegate { var error : NSError? let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted) - self.websocket.send(jsonData) - break + socket.write(data: jsonData) } + else { + print("Socket is not connected") + start() + } + } // MARK: helpers