diff --git a/Package.resolved b/Package.resolved index dd2116a..09afb3c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -3,11 +3,11 @@ "pins": [ { "package": "HandyJSON", - "repositoryURL": "https://github.com/alibaba/HandyJSON.git", + "repositoryURL": "https://github.com/symplicity/HandyJSON.git", "state": { "branch": null, - "revision": "dfab665e87f5dd56e16273222718ecd04a9a9d70", - "version": "5.0.1" + "revision": "d4cf79bac0fb92965fde311e0d2193245dde38b9", + "version": "5.0.6" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "f8657642dfdec9973efc79cc68bcef43a653a2bc", - "version": "8.0.2" + "revision": "72f5a90d573f7f7d70aa6b8ad84b3e1e02eabb4d", + "version": "8.0.9" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/Quick/Quick.git", "state": { "branch": null, - "revision": "33682c2f6230c60614861dfc61df267e11a1602f", - "version": "2.2.0" + "revision": "09b3becb37cb2163919a3842a4c5fa6ec7130792", + "version": "2.2.1" } }, { diff --git a/Package.swift b/Package.swift index 55cfe4c..c2a2cc8 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -11,11 +11,10 @@ let package = Package( targets: ["ScClient"]), ], dependencies: [ - // Dependencies declare other packages that this package depends on. .package(url: "https://github.com/daltoniam/Starscream.git", .exact("3.1.1")), - .package(url: "https://github.com/alibaba/HandyJSON.git", .exact("5.0.1")), - .package(url: "https://github.com/Quick/Quick.git", .exact("2.2.0")), - .package(url: "https://github.com/Quick/Nimble.git", .exact("8.0.2")), + .package(url: "https://github.com/symplicity/HandyJSON.git", .exact("5.0.6")), + .package(url: "https://github.com/Quick/Quick.git", .exact("2.2.1")), + .package(url: "https://github.com/Quick/Nimble.git", .exact("8.0.9")), .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "5.0.0") ], targets: [ diff --git a/ScClient.podspec b/ScClient.podspec index 705e858..49dd4ab 100644 --- a/ScClient.podspec +++ b/ScClient.podspec @@ -16,9 +16,9 @@ Pod::Spec.new do |s| # s.name = "ScClient" - s.version = "2.0.1" + s.version = "2.1.4" s.summary = "A socketcluster client for iOS and OSX." - s.swift_version = '4.0' + s.swift_version = '5.3' # This description is used to generate tags and improve search results. # * Think: What does it do? Why did you write it? What is the focus? @@ -84,7 +84,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/sacOO7/socketcluster-client-swift.git", :tag => s.version.to_s } + s.source = { :git => "https://github.com/Symplicity/socketcluster-client-swift.git", :tag => s.version.to_s } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # @@ -137,5 +137,5 @@ Pod::Spec.new do |s| # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } s.dependency "Starscream", "~> 3.1.1" - s.dependency "HandyJSON", "~> 5.0.1" + s.dependency "HandyJSON", "~> 5.0.6" end diff --git a/Sources/Main/main.swift b/Sources/Main/main.swift index 1eafc88..18c4fae 100644 --- a/Sources/Main/main.swift +++ b/Sources/Main/main.swift @@ -10,23 +10,23 @@ var onConnect = { var onDisconnect = { (client :ScClient, error : Error?) in - print("Disconnected from server due to ", error?.localizedDescription) + print("Disconnected from server due to ", error?.localizedDescription ?? "") } var onAuthentication = { (client :ScClient, isAuthenticated : Bool?) in - print("Authenticated is ", isAuthenticated) + print("Authenticated is ", isAuthenticated ?? false) } var onSetAuthentication = { (client : ScClient, token : String?) in - print("Token is ", token) + print("Token is ", token ?? "") client.subscribeAck(channelName: "yell", ack : { (channelName : String, error : AnyObject?, data : AnyObject?) in if (error is NSNull) { print("Successfully subscribed to channel ", channelName) } else { - print("Got error while subscribing ", error) + print("Got error while subscribing ", error ?? "") } }) @@ -36,7 +36,7 @@ var onSetAuthentication = { if (error is NSNull) { print("Successfully published to channel ", channelName) }else { - print("Got error while publishing ", error) + print("Got error while publishing ", error ?? "") } }) @@ -47,7 +47,7 @@ client.setAuthenticationListener(onSetAuthentication: onSetAuthentication, onAut client.onChannel(channelName: "yell", ack: { (channelName : String , data : AnyObject?) in - print ("Got data for channel", channelName, " object data is ", data) + print ("Got data for channel", channelName, " object data is ", data ?? "") }) client.connect() diff --git a/Sources/ScClient/Utils/Miscellaneous.swift b/Sources/ScClient/Utils/Miscellaneous.swift index d03129c..f5aa390 100644 --- a/Sources/ScClient/Utils/Miscellaneous.swift +++ b/Sources/ScClient/Utils/Miscellaneous.swift @@ -4,7 +4,25 @@ public class JSONConverter { public static func deserializeString(message : String) -> [String : Any]? { let jsonObject = try? JSONSerialization.jsonObject(with: message.data(using: .utf8)!, options: []) - return jsonObject as? [String : Any] + let jsonString = jsonObject as? [String : Any] + guard jsonString == nil else { + return jsonString + } + + let jsonString2 = jsonObject as? [[String : Any]] + guard let json = jsonString2 else { + return nil + } + + guard json.indices.contains(1), json.indices.contains(0) else { + return nil + } + + return [ + "data":json[1]["data"] ?? "", + "rid": json[0]["rid"] ?? 0, + "event": json[1]["event"] ?? "" + ] } public static func deserializeData(data : Data) -> [String : Any]? { diff --git a/Sources/ScClient/client.swift b/Sources/ScClient/client.swift index 4a2a1f7..33d004b 100644 --- a/Sources/ScClient/client.swift +++ b/Sources/ScClient/client.swift @@ -1,60 +1,61 @@ import Starscream import Foundation - +import HandyJSON public class ScClient : Listener, WebSocketDelegate { - + var authToken : String? var url : String? var socket : WebSocket var counter : AtomicInteger - + var subscriptions: Set = [] + var onConnect : ((ScClient)-> Void)? var onConnectError : ((ScClient, Error?)-> Void)? var onDisconnect : ((ScClient, Error?)-> Void)? var onSetAuthentication : ((ScClient, String?)-> Void)? var onAuthentication : ((ScClient, Bool?)-> Void)? - + public func setAuthToken(token : String) { self.authToken = token } - + public func getAuthToken () -> String?{ return self.authToken } - + public func setBasicListener(onConnect : ((ScClient)-> Void)?, onConnectError : ((ScClient, Error?)-> Void)?, onDisconnect : ((ScClient, Error?)-> Void)?) { self.onConnect = onConnect self.onDisconnect = onDisconnect self.onConnectError = onConnectError } - + public func setAuthenticationListener (onSetAuthentication : ((ScClient, String?)-> Void)?, onAuthentication : ((ScClient, Bool?)-> Void)?) { self.onSetAuthentication = onSetAuthentication self.onAuthentication = onAuthentication } - + public func websocketDidConnect(socket: WebSocketClient) { counter.value = 0 self.sendHandShake() onConnect?(self) } - + public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) { onDisconnect?(self, error) } - + public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) { if (text == "") { socket.write(string: "") } else { if let messageObject = JSONConverter.deserializeString(message: text) { if let (data, rid, cid, eventName, error) = Parser.getMessageDetails(myMessage: messageObject) { - + let parseResult = Parser.parse(rid: rid, cid: cid, event: eventName) - + switch parseResult { - + case .isAuthenticated: let isAuthenticated = ClientUtils.getIsAuthenticated(message: messageObject) onAuthentication?(self, isAuthenticated) @@ -68,7 +69,7 @@ public class ScClient : Listener, WebSocketDelegate { authToken = ClientUtils.getAuthToken(message: messageObject) self.onSetAuthentication?(self, authToken) case .ackReceive: - + handleEmitAck(id: rid!, error: error as AnyObject, data: data as AnyObject) case .event: if hasEventAck(eventName: eventName!) { @@ -76,18 +77,18 @@ public class ScClient : Listener, WebSocketDelegate { } else { handleOnListener(eventName: eventName!, data: data as AnyObject) } - + } - + } } } } - + public func websocketDidReceiveData(socket: WebSocketClient, data: Data) { print("Received data: \(data.count)") } - + public init(url : String, authToken : String? = nil) { self.counter = AtomicInteger() self.authToken = authToken @@ -95,7 +96,7 @@ public class ScClient : Listener, WebSocketDelegate { super.init() socket.delegate = self } - + public init(urlRequest : URLRequest, authToken : String? = nil, protocols : [String]? = nil) { self.counter = AtomicInteger() self.authToken = authToken @@ -103,24 +104,24 @@ public class ScClient : Listener, WebSocketDelegate { super.init() socket.delegate = self } - + public func connect() { socket.connect() } - + public func isConnected() -> Bool{ return socket.isConnected; } - + public func setBackgroundQueue(queueName : String) { socket.callbackQueue = DispatchQueue(label: queueName) } - + private func sendHandShake() { let handshake = Model.getHandshakeObject(authToken: self.authToken, messageId: counter.incrementAndGet()) socket.write(string: handshake.toJSONString()!) } - + private func ack(cid : Int) -> (AnyObject?, AnyObject?) -> Void { return { (error : AnyObject?, data : AnyObject?) in @@ -128,82 +129,97 @@ public class ScClient : Listener, WebSocketDelegate { self.socket.write(string: ackObject.toJSONString()!) } } - + + public func invoke(eventName: String, data : AnyObject?) { + let emitObject = Model.getEmitEventObject(eventName: eventName, data: data, messageId: counter.incrementAndGet()) + self.socket.write(string : emitObject.toJSONString() ?? "") + } + public func emit (eventName : String, data : AnyObject?) { let emitObject = Model.getEmitEventObject(eventName: eventName, data: data, messageId: counter.incrementAndGet()) self.socket.write(string : emitObject.toJSONString()!) } - + public func emitAck (eventName : String, data : AnyObject?, ack : @escaping (String, AnyObject?, AnyObject?)-> Void) { let id = counter.incrementAndGet() let emitObject = Model.getEmitEventObject(eventName: eventName, data: data, messageId: id) putEmitAck(id: id, eventName: eventName, ack: ack) self.socket.write(string : emitObject.toJSONString()!) } - + + public func subscribe(channel: String, data: HandyJSON) { + let subscribeObject = EmitEvent(event: "#subscribe", data: Channel(channel: channel, data: data as AnyObject), cid: counter.incrementAndGet()) + self.socket.write(string : subscribeObject.toJSONString() ?? "") + subscriptions.insert(channel) + } + public func subscribe(channelName : String, token : String? = nil) { let subscribeObject = Model.getSubscribeEventObject(channelName: channelName, messageId: counter.incrementAndGet(), token : token) self.socket.write(string : subscribeObject.toJSONString()!) + subscriptions.insert(channelName) } - + public func subscribeAck(channelName : String, token : String? = nil, ack : @escaping (String, AnyObject?, AnyObject?)-> Void) { let id = counter.incrementAndGet() let subscribeObject = Model.getSubscribeEventObject(channelName: channelName, messageId: id, token : token) putEmitAck(id: id, eventName: channelName, ack: ack) self.socket.write(string : subscribeObject.toJSONString()!) + subscriptions.insert(channelName) } - + public func unsubscribe(channelName : String) { let unsubscribeObject = Model.getUnsubscribeEventObject(channelName: channelName, messageId: counter.incrementAndGet()) self.socket.write(string : unsubscribeObject.toJSONString()!) + subscriptions.remove(channelName) } - + public func unsubscribeAck(channelName : String, ack : @escaping (String, AnyObject?, AnyObject?)-> Void) { let id = counter.incrementAndGet() let unsubscribeObject = Model.getUnsubscribeEventObject(channelName: channelName, messageId: id) putEmitAck(id: id, eventName: channelName, ack: ack) self.socket.write(string : unsubscribeObject.toJSONString()!) + subscriptions.remove(channelName) } - + public func publish(channelName : String, data : AnyObject?) { let publishObject = Model.getPublishEventObject(channelName: channelName, data: data, messageId: counter.incrementAndGet()) self.socket.write(string : publishObject.toJSONString()!) } - + public func publishAck(channelName : String, data : AnyObject?, ack : @escaping (String, AnyObject?, AnyObject?)-> Void) { let id = counter.incrementAndGet() let publishObject = Model.getPublishEventObject(channelName: channelName, data: data, messageId: id) putEmitAck(id: id, eventName: channelName, ack: ack) self.socket.write(string : publishObject.toJSONString()!) } - + public func onChannel(channelName : String, ack : @escaping (String, AnyObject?) -> Void) { putOnListener(eventName: channelName, onListener: ack) } - + public func on(eventName : String, ack : @escaping (String, AnyObject?) -> Void) { putOnListener(eventName: eventName, onListener: ack) } - + public func onAck(eventName : String, ack : @escaping (String, AnyObject?, (AnyObject?, AnyObject?) -> Void) -> Void) { putOnAckListener(eventName: eventName, onAckListener: ack) } - + public func disconnect() { socket.disconnect() } - + public func disableSSLVerification(value : Bool) { socket.disableSSLCertValidation = value } - + /** Uses the .cer files in your app's bundle */ public func useSSLCertificate() { socket.security = SSLSecurity() } - + /** You load either a Data blob of your certificate or you can use a SecKeyRef if you have a public key you want to use. - Parameters: @@ -213,6 +229,13 @@ public class ScClient : Listener, WebSocketDelegate { public func loadSSLCertificateFromData(data : Data, usePublicKeys : Bool = false) { socket.security = SSLSecurity(certs: [SSLCert(data: data)], usePublicKeys: usePublicKeys) } - + + public func isSubscribed(to channelName: String) -> Bool { + return subscriptions.contains(channelName) + } + + public func isAuthorized() -> Bool { + return authToken?.isEmpty == false + } }