Swift socket.io .emit() doesn't fire - ios

I'm new to sockets and i'm stuck with the following problem, i use this library to connect my app to my nodejs server.
I'd like to know why if i call socket.emit() in the following way doesn't work:
override func viewDidLoad() {
var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: "myhosturl")!)
socket.emit("ping","some data");
socket.connect();
}
It doesn't work neither swiping socket.emit and socket.connect functions but if I trigger the socket.emit function by clicking a button it works:
override func viewDidLoad() {
var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: "myhosturl")!)
socket.connect();
}
#IBAction func sendMessage(sender: AnyObject) {
socket.emit("ping","some data");
}
I think i'm missing something here, it would be great is someone could explain this to me

Sockets require a connection before emit is called. You should probably wait until there is a connection before u emit. Try this:
override func viewDidLoad() {
let socket = SocketIOClient(socketURL: NSURL(string: "myhosturl")!)
socket.on("connect") { _, _ in
print("socket connected")
socket.emit("ping", "data")
}
socket.on("ping") { _, _ in
print("ping received")
}
socket.connect()
}

Related

Socket io doesn't connect after calling disconnect function .i.e Connect -> Disconnect -> Connect won't work iOS swift

I am working on Chat engine using Socket io version 15.2.0,
When first time when I call socket.connect() it works properly, then if I call socket.disconnect() and then again try to call socket.connect() then socket fails to connect without any error or log. Then I have to kill the app and need to relaunch the app then socket connects without any issue.
following is my Shared class for Socket connect and disconnect
class SocketHelper {
static let shared = SocketHelper()
var socket: SocketIOClient!
let manager = SocketManager(socketURL: URL(string: ApiUrls.socketUrl)!, config: [.log(true), .compress, .connectParams(["token": Globals.shared.session.accessToken]), .secure(true), .forceNew(true)])
private init() {
socket = self.manager.defaultSocket
}
func getSocketWithLazyConnect() -> SocketIOClient {
if self.socket.status == .connected {
return self.socket
}
self.socket.connect()
return self.socket
}
func disconnect() {
self.socket.disconnect()
}
func isConnected() -> Bool {
return self.socket.status == .connected
}
}
Any help much appreciated...
Thank you guys but I figured out what I was doing wrong
I have one shared class named ChatEngine it handle all chat related socket events.
in that method while to disconnect from socket, I wrote a function switchOff
func switchOff() {
socket?.off(Constants.SocketEvent.connect)
SocketHelper.shared.disconnect()
}
as shown above I was calling off funtion for connect event and that's why after that when I called socket.connect, I didn't received any call back for that inside
self.socket?.on(clientEvent: .connect, callback: { (data, ack) in
})
I just removed off function call from my switchOff function and now it's working fine. Now my switchOff function is like this
func switchOff() {
SocketHelper.shared.disconnect()
}

How to fix "websocket is disconnected: Invalid HTTP upgrade" error using Starscream

I want to connect an iPhone to a web server running on my Macbook using MAMP and Starscream Library https://github.com/daltoniam/Starscream
Installed using Cocoapods and set up the web server with this URL:localhost:8888
But I'm having a lot of headaches because the code doesn't work... I followed all the guides online, the official documentation and so on but none of them helped me...
Whenever I run the code, the console shows this message: websocket is disconnected: Invalid HTTP upgrade
So maybe it's my server that's broken? I tried using instead ws://echo.websocket.org/ and it worked!!! So I thought... Because it finally showed me: websocket is connected
But at the same time it wasn't!!! In fact the method socket.isConnected gave me false... After that if I called socket.disconnect() it doesn't
work either, no messages show up.
import UIKit
import Starscream
class ViewController: UIViewController, WebSocketDelegate {
let socket = WebSocket(url: URL(string:"ws://echo.websocket.org/")!)//websocket is connected
/*
let socket = WebSocket(url: URL(string: "ws://localhost:8888/")!) //websocket is disconnected: Invalid HTTP upgrade
*/
override func viewDidLoad() {
super.viewDidLoad()
socket.delegate = self
socket.connect()
if socket.isConnected {
print("hi1") //it doesn't get printed
}
if !socket.isConnected {
print("hi2") //it gets printed
}
}
func websocketDidConnect(socket: WebSocketClient) {
print("websocket is connected")
}
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
if let e = error as? WSError {
print("websocket is disconnected: \(e.message)")
} else if let e = error {
print("websocket is disconnected: \(e.localizedDescription)")
} else {
print("websocket disconnected")
}
}
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
print("Received text: \(text)")
}
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
print("Received data: \(data.count)")
}
}
So I finally understood the problem: Apache doesn't support Web Sockets by default... that's why I got the error message. Then the code works with "ws://echo.websocket.org/". You just have to put all socket.x functions outside viewDidLoad() to make them work, for example socket.disconnect in a Disconnect Button, socket.write in a sendButton etc.
For example:
let socket = WebSocket(url: URL(string: "ws://echo.websocket.org/")!)
override func viewDidLoad() {
super.viewDidLoad()
socket.delegate = self
socket.connect()
}
#IBAction func sendButtonPressed(_ sender: UIButton) {
socket.write(string: textField.text!)
}

How to connect Socket in Swift 4

I want to connect my app to the socket here is the code :-
import UIKit
import SocketIO
class SocketIOManager: NSObject {
static let manager = SocketManager(socketURL: URL(string: "myURL")!)
static let socket = manager.defaultSocket
class func connectSocket() {
let token = UserDefaults.standard.getAccessToken()
self.manager.config = SocketIOClientConfiguration(
arrayLiteral: .connectParams(["token": token]), .secure(true) // problem is here in passing token value
)
socket.connect()
}
class func receiveMsg() {
socket.on("new message here") { (dataArray, ack) in
print(dataArray.count)
}
}
}
I created a new class SocketIOManager.swift and I invoke my function in view controller
SocketIOManager.connectSocket()
and I am not using the localhost url but still my app is not connected to the socket I think I followed this How to connect with Socket.io? Swift 4 and I also add App Transport Security in info.plist. Any help? The problem is when I am passing the token value it gives me error otherwise it is working properly. Should I need to pass token when using socket?
You should make a shared property in your SocketIOManager class like this:
static let shared = SocketIOManager()
and then create init() like this:
var socket: SocketIOClient!
// defaultNamespaceSocket and swiftSocket both share a single connection to the server
let manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [.log(true), .compress])
override init() {
super.init()
socket = manager.defaultSocket
}
and finally write your methods like this:
func connectSocket() {
let token = UserDefaults.standard.getAccessToken()
self.manager.config = SocketIOClientConfiguration(
arrayLiteral: .connectParams(["token": token]), .secure(true)
)
socket.connect()
}
func receiveMsg() {
socket.on("new message here") { (dataArray, ack) in
print(dataArray.count)
}
}
and call your method like this:
SocketIOManager.shared.connectSocket()
The point is that you should make a strong reference to manager property in your viewController and static let shared = SocketIOManager() you do this for you!
Is your URL secured under HTTPS?
If it is not, that could be the problem, due to App Transport Security restrictions.
See Info Plist Reference
See also this post here.
If that's not the problem. I'd suggest to check if your Socket version on both sides, server and client, are the same. Recently I had a problem because of that.
Hope it helps!
try this Example:
let manager = SocketManager(socketURL: URL(string:
"http://xxxxxxxxx.com")!, config: [.log(true), .compress])
var socket = manager.defaultSocket
socket.connect()
socket.on(clientEvent: .connect) {data, ack in
print("socket connected")
self.gotConnection()
}
}
func gotConnection(){
socket.on("new message here") { (dataArray, ack) in
print(dataArray.count)
}
}

How can I call asynchronously a method only when other async method finishes working in Swift?

I'm implementing socket.io library for swift.
There is a method related to connecting to the server that looks as follows:
func connectToServerWithNickname(nickname: String) {
socket.emit("connectUser", nickname)
}
The socket.emit calls:
private func _emit(data: [AnyObject], ack: Int? = nil) {
dispatch_async(emitQueue) {
guard self.status == .Connected else {
self.handleEvent("error", data: ["Tried emitting when not connected"], isInternalMessage: true)
return
}
let packet = SocketPacket.packetFromEmit(data, id: ack ?? -1, nsp: self.nsp, ack: false)
let str = packet.packetString
DefaultSocketLogger.Logger.log("Emitting: %#", type: self.logType, args: str)
self.engine?.send(str, withData: packet.binary)
}
}
as you can see it's all packed in dispatch_async. I would like to post an NSNotification as soon as this method is done and my app connects to the server, sth like:
NSNotificationCenter.defaultCenter().postNotificationName(setConnectionStatus, object: self)
So my question is - how should I modify my connectToServerWithNickname method so that it calls my nsnotificationcenter when everything goes correct?
After you stated you are using socket.IO
When you make your instance of the socket before you connect you will have
var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: "http://192.168.1.XXX:3000")!)
then after this put
socket.on("connect") { (data, ack) -> Void in
// This will be fired once your socket connects.
}
EDIT:
For individual emits, you would keep a pointer to your socket and use
let json = ["things_i_need_to_send" : stuff]
socket.emitWithAck(Place_i_need_to_send_to, json)(timeoutAfter: 3) { data in
//This is called on completion
}
So using your above example.
socket.emitWithAck(connectUser, nickname)(timeoutAfter: 3) { data in
//This is called on completion
}

Implementing A UDP "Listener" In Swift?

I've been trying to somewhat reverse engineer a project to discover Logitech Harmony Hub devices on my network, and posted this question to see if someone could help me understand UDP broadcast. The answer explained that I've implementing the send portion of the UDP broadcast, but I've not implemented anything to "listen" for responses. And that's where I'm struggling. Here's my send code;
import UIKit
import CocoaAsyncSocket
class ViewController: UIViewController, GCDAsyncUdpSocketDelegate {
var address = "255.255.255.255"
var port:UInt16 = 5224
var socket:GCDAsyncUdpSocket!
var socketReceive:GCDAsyncUdpSocket!
var error : NSError?
override func viewDidLoad() {
super.viewDidLoad()
let message = "_logitech-reverse-bonjour._tcp.local.\n61991".dataUsingEncoding(NSUTF8StringEncoding)
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
socket.sendData(message, toHost: address, port: port, withTimeout: 1000, tag: 0)
do {
try socket.enableBroadcast(true)
} catch {
print(error)
}
}
func udpSocket(sock: GCDAsyncUdpSocket!, didConnectToAddress address: NSData!) {
print("didConnectToAddress");
}
func udpSocket(sock: GCDAsyncUdpSocket!, didNotConnect error: NSError!) {
print("didNotConnect \(error)")
}
func udpSocket(sock: GCDAsyncUdpSocket!, didSendDataWithTag tag: Int) {
print("didSendDataWithTag")
}
func udpSocket(sock: GCDAsyncUdpSocket!, didNotSendDataWithTag tag: Int, dueToError error: NSError!) {
print("didNotSendDataWithTag")
}
func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {
var host: NSString?
var port1: UInt16 = 0
GCDAsyncUdpSocket.getHost(&host, port: &port1, fromAddress: address)
print("From \(host!)")
let gotdata: NSString = NSString(data: data!, encoding: NSUTF8StringEncoding)!
print(gotdata)
}
}
I see I have the code to handle the response (in didReceiveData), but I'm unsure what I need to implement to get the listening going;
Do I need to "bind" to the listener port (in this case, 61991)?
Do I need to "join the multicast group"? And if so, at what address? I tried doing so at "255.255.255.255", which creates a setSocketOpt() error when I build.
I know I need to call beginReceiving(), and can I do all this on socket, or do I need to instantiate a separate socket for the listening?
Edit: Resolved
The below answer absolutely helped me to solve the problem. It seemed I wasn't getting a response because I had not fully implemented a means of handling the incoming response.
Per the code provided in the answer below, I added the following;
// Setup the other socket (used to handle the response from the Harmony hub)
otherSocket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
do {
// Accept connections on port 61991
try otherSocket.acceptOnPort(61991)
} catch {
// Handle any errors here
print(error)
}
I also set this controller to be a GCDAsyncSocketDelegate, which seemed to do the trick. I was able to read the response in didReadData.
The following code changes enabled me to receive UDP packets that I sent from my Mac using netcat, but my Harmony hub didn't seem to send anything, so I am not sure if the data that is being sent is correct.
override func viewDidLoad() {
super.viewDidLoad()
let message = "_logitech-reverse-bonjour._tcp.local.\n61991".dataUsingEncoding(NSUTF8StringEncoding)
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
do {
try self.socket.bindToPort(61991)
try self.socket.beginReceiving()
try socket.enableBroadcast(true)
socket.sendData(message, toHost: address, port: port, withTimeout: 1000, tag: 0)
} catch {
print(error)
}
}
From the command line you can test receiving using the command
echo -n "hello" | nc -4u -w1 x.x.x.x 61991

Resources