Swift Socket.io the event sent right after connection is not executed - ios

So I am trying to send (emit) an event right after the connection.
I am building a chat app, and the only issue I have is that when the user disconnected and connect to the server again, other clients are not informed the reconnected client is back (I think it is because my userlist data at the server end is not updated when the reconnected client is back) So I'm trying to send an event right after the connection to update the userlist data at the server end. But seems like the event is not executed for some reason. Any help would be great!
func establishConnection() {
socket.connect()
if UsersViewController.nickname != nil {
socket.emit("connectWithNewId", UsersViewController.nickname)
}
}
I have check already that the event can be send from the other place of the code. It just seems not working right after the connection.

You should emit the event when the connection is established, try something like this:
func establishConnection() {
socket.on("connect") { data, ack in
if UsersViewController.nickname != nil {
socket.emit("connectWithNewId", UsersViewController.nickname)
}
}
socket.connect()
}

Related

Problems subscribing to a room (socket) with Socket.IO-Client-Swift and Swift, Sails.js on the Server

On Swift, I use
socket.on("test") {data, ack in
print(data)
}
In order to subscribe to a room (socket) on my Sails.js API.
When I broadcast a message from the server, with
sails.sockets.broadcast('test', { text : 'ok' })
the socket.on handler is never called.
However, if I set "log" TRUE to config when connecting my socket.io client from swift, in Socket-IO logs the message arrives.
What's wrong?
Eventually, I found my mistake:
The whole process I did is right:
(The request to join the room is done by the server, with sails.sockets.join)
Wrong thing was using socket.on with the ROOM NAME parameter.
I will explain it better, for others having same problem:
From Swift you should subscribe by making a websocket request to an endpoint on the server that accepts websocket requests (GET, POST, PUT). For example, you can make a POST request, passing in the room name into the body.
socket.emitWithAck("post", [
"room": "testroom",
"url": "/api/v1.0/roomsubscribing"
]).timingOut(after: 0) {data in
print("Server responded with \(data)")
}
On server side, inside the room-subscribing endpoint, you should have the following code:
roomSubscribing: function(req, res) {
if (!req.isSocket) {
return res.badRequest();
}
sails.sockets.join(req, req.params('room'), function(err) {
if (err) {
return res.serverError(err);
}
});
}
When the server want to broadcast some data to subscribers of the "testroom" room, the following code must be used:
sails.sockets.broadcast('testroom', { message: 'testmessage' }
Now on the swift's side you must use:
socket.on("message") { data, ack in
print(data)
}
in order to get the message handler to work. I thought you should use room name, instead you should use the KEY of the KEY/VALUE entry you used in your server when you broadcasted the data (in this case, "message").
I only have a small amount of experience with sockets, but in case nobody else answers...
I think you are missing step one of the three step socket process:
A client sends a message to the server asking to subscribe to a particular room.
The client sets up a socket.on to handle particular events from that room.
The server broadcasts an event in a particular room. All subscribers/clients with a .on for that particular event will react.
I could be wrong, but it sounds from your description like you missed step one. Your client has to send a message with io.socket, something like here, then your server has to use the socket request to have them join the room, something like in the example here.
(the presence of log data without the socket.on firing would seem to confirm that the event was broadcast in the room, but that client was not subscribed)
Good luck!

Connecting to MQTT happens after publish due to async

I'm using CocoaMQTT to connect and publish events. To publish, its important that I am connected to MQTT. But connect is async and so is publish. I want it to be sequential that first it should connect and once connected then publish so for example if the connect happens after 5mins then publish should happen after that.
https://github.com/emqtt/CocoaMQTT
How exactly can I do that?
Here is my code:
class ViewController: UIViewController {
var iot: MQTTDelegate!
#IBAction func click(_ sender: Any) {
self.iot = IoT(
withClientId: "clientId",
host: "host",
port: 1883
)
iot.connect(username: "username", password: "token")
iot.publish(topic: "hello", message: "")
}
}
If I move connect to a separate button then it works as first I click the connect button and then I click the "click" button so its sequential. How do I make the above code sequential?
I used PromiseKit to promisify the code but I dont know what exactly to fulfill to connect.
It looks like you are trying to establish a connection to an MQTT server and publish a message to a topic, but the connection is being established after the message is published due to asynchronous behavior.
To fix this issue, you can use the connect() method of the MQTTClient class to establish the connection before publishing the message. The connect() method takes a completion block that is called when the connection is established or if an error occurs.
Here is an example of how you could modify your code to establish the connection before publishing the message:
client.connect { error in
if error == nil {
// Connection was successful
client.publish("topic", withString: "Hello, World!")
} else {
// There was an error connecting to the MQTT server
}
}
This way, the connection to the MQTT server will be established before the message is published, and the message will be sent as soon as the connection is ready.

How can I wait for Socket.IO to have a socket connection established before connecting a user to the server?

I'm running SocketIO-Client-Swift in order to open WebSockets and communicate with a server. However the problem I'm having is that I want to connect to the socket and then connect to the server with an emit.
SocketIOManager.sharedInstance.connectToSocket()
SocketIOManager.sharedInstance.connectToServer(self.myUsername)
And those point to functions here:
func connectToSocket() {
addHandlers()
socket.joinNamespace("/chat")
socket.connect()
}
func addHandlers() {
self.socket.on("connectUser") { data in
print("socket connected")
}
}
func connectToServer(username: String) {
socket.emit("connectUser", username)
}
Even after registering the handle, the emit is being called before the socket is properly connected. The connect works fine after the connect has been established as I can properly send messages and get feedback after my Log shows a connection.
You need to wait for the client to receive the connect event. That will signal that it is now connected and you can safely send initial data on the connection from that event handler.

Socket.io does not close the socket on demand (and does not allow my swift app to reconnect again to the node.js server)

I am playing around with socket.io library, I already implemented the server side (in node.js). I try to follow a very simple scenario:
1) user establishes connection
2) user connects to the room
3) user disconnects
4) user reconnects
I'm using ios app (written in swift) on a client side.
My server code is as follows:
io.on('connection', function(clientSocket){
console.log('a user connected here');
clientSocket.on('disconnect', function(){
console.log('user disconnected');
});
clientSocket.on('connectUser', function(username){
clientSocket.join(username);
console.log('user ' + username + ' connected to the room.');
});
Now in my client side I created a class responsible for handling all socket stuff:
class SocketIOManager: NSObject {
static let sharedInstance = SocketIOManager()
var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: serverURL)!, options: [.ForceNew(true)])
override init() {
super.init()
}
func establishConnection() {
socket.connect()
}
func closeConnection() {
socket.disconnect()
}
func connectToServerWithNickname(nickname: String) {
print("CONNECTING TO SOCKET SERVER \(nickname)")
socket.emit("connectUser", nickname)
}
}
Now - further on - in my swift app (in applicationDidBecomeActive) I'm calling establishConnection() and then SocketIOManager.sharedInstance.connectToServerWithNickname(username).
In my server console I see:
a user connected here
user 571fc6818451630f5becda9c connected to the room.
In my appDelegate I implemented this function:
func applicationWillResignActive(application: UIApplication) {
SocketIOManager.sharedInstance.closeConnection()
print("closing sockets")
}
so now when I put my app in the background I see closing sockets and in my server console I see user disconnected.
And now to the main problem - I want to reconnect user when he comes back to the app. Therefore I implemented this method:
func applicationDidBecomeActive(application: UIApplication) {
print("application is back to life")
SocketIOManager.sharedInstance.establishConnection()
SocketIOManager.sharedInstance.connectToServerWithNickname(username)
}
But now when I wake up the app from the background, in my server console I only see a user connected here. So this message:
user 571fc6818451630f5becda9c connected to the room.
is missing and I assume it's because my app is not reconnecting to the socket.
Now from what I've read, socket.io is really buggy and people recommend switching to other libraries, but I would like to use it anyway. I found this issue on github https://github.com/socketio/socket.io-client/issues/251 and they recommend to use a specific parameter on client side. Following this plugin's page for swift https://github.com/nuclearace/Socket.IO-Client-Swift I found the specific flag:
case ForceNew(Bool) // Will a create a new engine for each connect. Useful if you find a bug in the engine related to reconnects
and that is why I'm constructing my socket on client side as:
var socket: SocketIOClient = SocketIOClient(socketURL: NSURL(string: serverURL)!, options: [.ForceNew(true)])
but yeah, even in the official code of the Socket.IO-Client-Swift library there's a comment above the disconnect method:
/// Disconnects the socket. Only reconnect the same socket if you know what you're doing.
/// Will turn off automatic reconnects.
public func disconnect() {
DefaultSocketLogger.Logger.log("Closing socket", type: logType)
reconnects = false
didDisconnect("Disconnect")
}
Does anyone know how could I handle that? I want to close the socket each time my app goes to the background and connect to it again when my app goes to the foreground. Thanks!
add your code in applicationWillEnterForeground
SocketIOManager.sharedInstance.establishConnection()
SocketIOManager.sharedInstance.connectToServerWithNickname(username)

Repeat an asynchronous request if it fails?

I am writing an ios app that relies on being able to tell when a user is connected to wifi and, when he or she connects or disconnects, send an asynchronous request using alamo fire.
The first time I connect, my asynchronous succeeds.
However, after I first connect, any toggling of the wifi results in 404s.
I suspect this is because I am sending the request as soon as the user connects/disconnects, meaning that for a brief moment he or she has no internet service.
My question is, can I repeat the request if it fails or is it possible to "cache" the requests I want to make and wait until the user has internet connection to make them?
There are many solutions to solve this. One is to call the download method recursively again and so implementing an automatic retry mechanism on errors:
func downloadSomething() {
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.response { request, response, data, error in
if let error = error {
log(error)
self.downloadSomething() // recursive call to downloadSomething
} else {
// do something on success
}
}
}
You can extend this by:
showing the user also an altert view asking him if he want's to retry
the download or not before retrying the download. (depending on your
UI strategy on network errors)
a specified count of automatic re-trys and then ask the user.
checking the error status code and then depending on the code do
different network error handling strategies...
etc...
I think there is no needed to re-invented apple code like reachability or this swift reachability porting. You can able to check if a user is connected to the net or wifi very easily:
class func hasConnectivity() -> Bool {
let reachability: Reachability = Reachability.reachabilityForInternetConnection()
let networkStatus: Int = reachability.currentReachabilityStatus().rawValue
return networkStatus != 0
}
For a Wi-Fi connection:
(reachability.currentReachabilityStatus().value == ReachableViaWiFi.value)

Resources