Integrating socket.io into REST API - ios

so i'm building an iOS app and i've started building a REST API w/ nodejs, express, & mongodb. I'm currently adding instant messaging and notifications to my app so i've been reading up on websockets(socket.io). After tons of reading, I honestly cannot wrap my head around the concept and how to integrate into my API.
For example, I have this API route:
// create new message
app.post('/newmessage', function (req, res, next) {
if (!req.body.message ) {
res.json({success: false, msg: 'You must type a message.'});
console.log('message: ' + req.body.message);
} else {
var newMessage = new Message({
fromUser: ObjectID(req.params.id),
toUser: ObjectID(req.params.id),
message: String,
});
// save new message
newMessage.save(function(err) {
if (err) {
res.json({success: false, msg: 'message was unsuccessful.'});
} else {
res.json({success: true, msg: 'message sent!'});
console.log(newMessage.createdAt);
console.log(newMessage.updatedAt);
}
});
}
});
How would I integrate socket.io into this specific call? Would I create a Socket.js file and export from there? Backend isn't my thing at all, so I apologize if this is a poor question. Thanks!

The general architecture for using a webSocket or socket.io connection for instant messaging or server-push notifications is as follows:
Each client makes a webSocket or socket.io connection to the server.
The server is listening for those incoming connections and uses some sort of cookie on the initial connection to identify which user is connecting.
The server then holds those connections open for the duration of the user session and listens for incoming messages on them.
At any time, if the server wishes to send a notification to the client, it can find the connection belonging to the desired client and send a message on that connection.
The client will be listening for incoming messages and will receive that message and can then process it.
For instant messaging, a client would send a message to the server that essentially says "send this message to Bob" (where Bob is some user ID for some other user on the system). The server would receive that message from the other client and would then find Bob's connection and send the message to Bob on that connection.
I would recommend using socket.io as it offers a number of useful features on top of webSockets and there should be socket.io libraries for all platforms you would be using. The socket.io documentation includes a demo app that does chat which will give you some idea how things work with socket.io.

Related

How to detect if ActionCable subscription request failed

I've recently deployed a Rails app, and have heard back from a few users that it doesn't work in their work environments. It's clear that Websockets are blocked for them.
I've hunted through the Rails documentation on this but can't find any information on how to go about detecting if this has happened. My best guess would be to send an AJAX request out for an ActionCable broadcast, and when that broadcast is not received after a certain timeout, to conclude that Websockets must be blocked.
Are there any easy answers here, perhaps already part of the Rails API, to determine Websocket connectivity?
I have a workaround that isn't great, but is better than anything else I've seen. Rails provides no interface, but you can get down to the native WebSocket and handle the error.
consumer.subscriptions.create("ChatChannel", { ... });
consumer.connection.webSocket.onerror = function () {
console.log('Websocket connection error!');
// Error handling goes here. Fallback to polling, perhaps.
};
ActionCable will keep trying to reconnect, and this only grabs the first failure, but that's enough to cover many cases.
There is a rejected handler you can use. This should fire when the subscription is rejected by the server.
The below coffeescript example is from the official rails docs.
App.cable.subscriptions.create "AppearanceChannel",
# Called when the subscription is ready for use on the server.
connected: ->
// Do something
# Called when the subscription is rejected by the server.
rejected: ->
// Do something
Whenever an action cable connection fails it writes to the browser console failed: WebSocket is closed before the connection is established
You can leverage this to know if there was a connection error:
def action_cable_connection_errored?
page.driver.browser.manage.logs.get(:browser)
.map(&:message)
.join
.match('failed: WebSocket is closed before the connection is established')
end

Token expiration in Twilio

I'm working on embedding a soft phone into a web page that will go into Odoo (web based ERP system). It will allow inbound and outbound calls for employees.
The token expires every hour. So this means the user will have to refresh the page every hour. I could do an http refresh but if the user is on a call when it does the refresh it will knock them off the call.
How do we get around this so we can build a fully working dialer?
Twilio evangelist here.
I'd suggest using JavaScript to do an asynchronous HTTP request to get a new token from your server and then updating the instance of client with it.
Hope that helps.
Another Twilio evangelist here!
You can actually listen for the offline event on the Twilio.Device object. From the documentation:
.offline( handler(device) )
Register a handler function to be called when the offline event is
fired. This is triggered when the connection to Twilio drops or the
device's capability token is invalid/expired. In either of these
scenarios, the device cannot receive incoming connections or make
outgoing connections. If the token expires during an active connection
the offline event handler will be called, but the connection will not
be terminated. In this situation you will have to call
Twilio.Device.setup() with a valid token before attempting or
receiving the next connection.
So you want something like:
Twilio.Device.offline(function(device) {
fetchTokenFromServer(function(token) {
device.setup(token);
});
});
where fetchTokenFromServer makes the HTTP request that Devin suggested in his answer.
Let me know if this helps.
I just ran into this issue so hopefully my solution can help you and others.
I was using twilio.js v1.3 and tried implementing my offline callback like #philnash recommended, but kept getting the error device.setup is not a function. I then tried using Twilio.Device.setup(newToken) and was able to get the capability token refreshed but also ended up getting a new error: Cannot read property 'setToken' of undefined.
I ended up having to use twilio.js v1.4 to make the error go away. My working solution looks like this:
Twilio.Device.offline(function(device) {
$.ajax(urlToGetNewToken, type: 'get').done(function(newToken) {
Twilio.Device.setup(newToken)
})
})

Migrating Parse Push to AWS

I am having an issue with Parse Push on AWS. We have the adapter configured per these specifications and can seem to broadcast to one specific user (don't know how or why that user) using the curl method, but I am confused how (or if) I can use this to send user to user push notifications. For example, "X liked Y's Z" Where X is the liker, Y is the liked and Z is the object being liked.
in parse you have multiple options to send push notifications.
Push notifications can be sent to one or more users by providing a query with all the installations that you like to send the push to. You can also send push notifications for specific channel that the user subscribed to, this is very good for marketing or maybe if your app have different type of users (for example: sellers, buyers etc.)
The reason that a push is being sent to installations and not to users is because that one users can have multiple installations (e.g. iphone,ipad,other device etc.)
like i said there are multiple options to send push but i recommend to send a push using one of the following approaches:
From cloud code - you can create cloud code function that will be triggered by the client and this cloud code function will first create a query of all the installations that you need to send the push to and will execute the function that will actually send the push. This cloud code function can receive multiple parameters that can contain any data that needs to be processed before you sending the push, such data can be array of users, channel name and more.
the following cloud code snippets show how to send a push for all users who successfully subscribed and have device token:
Parse.Cloud.afterSave("SendPush", function(request) {
var query = new Parse.Query(Parse.Installation);
query.exists("deviceToken");
var payload = {
alert: "after save push"
};
Parse.Push.send({
data: payload,
where: query
}, {
useMasterKey: true
})
.then(function() {
response.success("Push Sent!");
}, function(error) {
response.error("Error while trying to send push " + error.message);
});
});
and then from your iOS SDK you call this cloud code function in the following way:
NSDictionary * parameters = # {}; // put parameters if required
[PFCloud callFunctionInBackground: #"SendPush"
withParameters: parameters block: ^ (id _Nullable object, NSError * _Nullable error) {
// callback result
}
];
From iOS SDK - if you don't want to send your push from cloud code you can do it directly from your iOS. I think it's better to do it in cloud code because in cloud code you write ones and then you can trigger this function from any SDK and also from the REST API.
In Parse docs you can find a lot of examples on how to send push from iOS SDK all the examples can be found here:
http://parseplatform.github.io/docs/ios/guide/#push-notifications
but like i said the best is to do it with cloud code.
Answer is similar if you're using AWS services without Parse. A better architecture is where the mobile device invokes business logic in the cloud (i.e., in an AWS Lambda function) and that code sends the push notifications. This allows you to build a more secure solution because you can control content and control who can send to whom (i.e., you must be on someone's friend list to send) within your business logic. If you open permissions to publish directly from the device, then you make system vulnerable to attacks where someone uses the app's credentials and publishes harmful content (potentially to all your app's users).
Example of sending push notification from an AWS Lambda function...
Can you send SNS push notification from lambda function in Amazon AWS?

Twilio iOS Video Call: Getting "User is unavailable" error message when user rejects the call

I am implementing twilio's video call in my iOS application. The problem is that I am looking for a way to know when the counterpart application is dead to send him a VoIP Push notification.
The solution I was trying to implement was that, when the call returns "User is unavailable" error then i would tell my backend to send VoIP notification to the counterpart, the problem with this solution is that I found a twilio's bug where sometimes if the user rejects the call twilio's SDK returns a wrong error message saying "User is unavailable" instead an error with "User rejects the call" message. So I can't know if the user was really unavailable (to send the VoiP notification) or if the user just rejected the call
How to reproduce the error?
1. Connect two clients with fixed identity id. For example "identity1" and "identity2"
2. Make a call from "identity1" to "identity2" and rejects it from "identity2". You will receive the correct error message "User rejects the call"
3. Close the app in "identity2" WITHOUT CALLING UNLISTEN, just kill the app.
4. Then start the app again in "identity2" (change the token if you want but let the same identity id).
5. Make a call from "identity1" to "identity2" and rejects it from "identity2". You will receive the wrong error message "User is unavailable" instead "User rejects the call".
Thats the problem is like twilio would not remove the old client's instance if we don't call unlisten. And if I can't difference when user is unavailable or when just rejects the call then I can't send the VoIP push when is really needed.
In order to receive incoming call, you have to call listen API on each launch of the app. It seems you might be killing the app after listen but after relaunch listen was not called on client. So when the remote party makes an outbound call, it is getting TWCErrorCodeConversationParticipantNotAvailable.
Once conversation client starts listening for incoming calls, remote party should receive TWCErrorCodeConversationRejected on reject.
In other words, if A calls B, and B is not listening (i.e. not called listen on client), A will receive “user is unavailable".
The example in Swift:
/* Create an AccessManager - this provides a single place to update your Twilio
Access Token when using multiple Twilio SDKs */
var accessManager = TwilioAccessManager(token:self.accessToken, delegate:self)
// Create a Conversations Client and listen for IncomingInvites
var client = TwilioConversationsClient(accessManager: accessManager, delegate: self)
client!.listen()
// MARK: TwilioConversationsClientDelegate
// Selectively handle IncomingInvites based on the originator
func conversationsClient(conversationsClient: TwilioConversationsClient,
didReceiveInvite invite: TWCIncomingInvite) {
if (invite.from == "ringo") {
invite.reject()
} else {
/* See the "Specify Local Media Constraints when Creating a
Conversation" guide for instructions on constructing LocalMedia */
invite.acceptWithLocalMedia(self.localMedia!) { conversation, error in
self.conversation = conversation
self.conversation!.delegate = self
}
}
}
Please let me know if this helps at all!

Websocket in Rails app on Heroku is not working

I added a chat functionality in my existing RoR app with the websocket-rails (https://github.com/websocket-rails/websocket-rails) gem and I'm testing it in production mode on Heroku.
It works fine in localhost, but when I sent it to Heroku it is not receiving any further messages from the Server after the connection.
The connection works:
var WEBSOCKET_URL = 'myapponheroku.herokuapp.com/websocket'
var dispatcher = new WebSocketRails(WEBSOCKET_URL);
dispatcher.on_open = function(data) {
console.log('Connection has been established: ', data);
};
And I see the feedback message.
But the first task is to ask the server for active rooms in my chat. I noticed nothing is returning. I put some feedback messages in the server's action that should do the first tasks and none of them are reached. The feedback I implemented is a simple pair of functions that just returns a object and prints it to the console:
Javascript
dispatcher.bind('feedback',function(data){
console.log(data);
});
Ruby
def feedback data
send_message :feedback, {data: data}
end
It is never triggered. Any ideas? Is the a special configuration I must do on Heroku to allow it to work?
#update
Same error on my live server hosted on AWS.
Connection is established, but nothing is returned from server to client.
#update2
I wrote some code so the app would write some files when connection is established and when it asks for open rooms, right at the beginning, and the files were not created. The actions are not even being called.

Resources