I'm using XMPPFramework in my iOS app to send and receive command messages between client and server. The server would answer to different command messages immediately.
Sometime the connection is very slow, the message sent from client would have no response for a long time. So I want to know how can I send an XMPP message with timeout or I have to implement this myself.
Anyway:
This goes to - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message.
Using Reachability, the internet is still reachable via [[Reachability reachabilityForInternetConnection] isReachable].
Why not just set a timer when you send the message, override the response callback on the XMPPStream, and see which is called first?
Related
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!
I'm using NSURLConnection to send an HTTP request and running it with
[[NSURLConnection alloc] initWithRequest:self.request delegate:self];
where "self.request" is a configured NSMutableURLRequest object. Upon a network failure and callback to
-(void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
I'd like to cancel the request and write the payload to file to be uploaded later. However if I reconnect too quickly the payload ends up getting sent still (and I later send the same payload from the file). In the failure callback, I've tried to use
[connection cancel];
But the http request still goes through upon reconnecting within a few seconds. Is this due to some retry mechanic that I can disable?
From Apple's docs on didFailWithError:
// Once the delegate receives this message, it will receive no further messages for connection.
So I'd say you have a bug somewhere else in the code. Once that delegate message is called, the connection is done and it will never be restarted unless you do so.
From the question it seems like you are trying to cancel the connection in the failure delegate method, is that correct? That should not be necessary since the connection is already dead. I'm also a bit confused when you say you "retry to quickly". I don't see any "retry" code so it would help if you could post that too.
Can you post more code to help diagnose the issue?
I've got NSURLConnection with timeout = 30s which is uploading an image on server.
If connection is horrible and call delegate method didFailWithError: then i need to cancel current connection.
But if i just call the [myConnection cancel] connection will still alive but will not call delegates methods (apple docs say it - NSURLConnection cancel method). And i want to abort connection but not only remove delegate methods. How i can do what?
upd:
My problem is if connection is fails by timeout - in business logic i must recreate connection with similar request. If i have got horrible connection for 1 min and after that connection will be good - server will get a lot of (about 3 times retry count) photos. But first 2 connections is canceled. –
At the moment i make "dirty hack" like "if it's photo uploading request" - do not retry recreate connection.
I do a ton of network stuff and don't recall a scenario where everything was successfully received but the iOS app timed out. I'm trying to grok the scenario you describe, where you're seeing this happen a lot and I'm not seeing how that would happen. We might need to see some of your code.
Regardless, when you cancel a NSURLConnection, it not only stops the delegate methods from being called, but it stops the upload, too. I just did a test:
I attempting to upload a 20mb file (non-chunked request);
At the 1mb mark (as identified by didSendBodyData), I canceled the connection (by calling [connection cancel]);
I immediately stopped receiving any delegate messages at that point;
Looking at Charles, I'm only seeing 1.3mb of data in the hex log of the request. When I look at the "Network" tab of the Mac OS "Activity Monitor" and looking at by "Sent Bytes", it's at 2.1mb uploaded.
So canceling a connection will stop further data from being sent. Perhaps if there is some transmission in progress that still gets out (that's the asynchronous world we live it), but the it's not true to conclude that canceled connections will routinely send their full HTTP request. There must be something about the nature of the timeout that is unique to your environment.
In terms of your immediate problem, I might suggest that when uploading a file that the iOS app assign some unique identifier to the upload so that the server code can immediately recognize duplicate requests and handle them appropriately. But the question is why you are seeing so many time-outs and notably ones where the request appears to be successfully received in toto, but the response is not. That's very curious.
You cannot forcefully abort an ongoing connection.
In case if connection is not yet started cancel and unscheduleFromRunLoop for the NSURLConnection will work.
Try with following step
[myConnection cancel];
myConnection = nil;
Might be helpful in your case and If this step is not working then also try with myConnection.delegate = nil;
For fun I've built a little socket based chat application. It is speaking over tcp socket with a nodejs server. Its successfully sending messages back and forth. However, the problem occurs when i press the home button and put my app in the background - at this point the socket seems to be "paused" and all messages that are sent during this time are lost.
I've got push notifications setup for when the app is in the background but since the socket is not active in the app the message never really reaches the app.
How should i handle this? What are my options?
Ive got a few ideas but id love some input here on how people are handling this.
My ideas:
Add a "loadHistory" method on my nodejs server that sends all the history of a channel. So that my app can call a certain URL and get a
JSON formated response with all the messages. Maybe this should be done each time a table item is touched?
Make use of the new iOS7 background running features? (Im not to sure its possible to keep the socket alive with that)
Implement a way for the server to know if a message has been delivered, if it could not be delivered it stores the message on the
database. As soon as the client goes online again it sends the
messages over tcp. This would require a lot of added functionality to
the server so id like to avoid this if possible.
if you want offline message , the third way is needed, for every message you get, you need to send a notify to the server to tell it the message is delivered , and when you get connected , you should fetch the lost message.
you could use voip, but your app can not receive message if it was killed.
(if you add voip mode but your app is not about voice, apple will block your app)
I have also done some work over TCP socket. I gave background support by these steps :
Add "Required background modes" key in your plist file.
Add this code in your AppDelegate.m file
- (void)applicationDidEnterBackground:(UIApplication *)application
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000
if([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)] && [[UIDevice currentDevice] isMultitaskingSupported])
{
NSLog(#"Keep timeout alive");
[application setKeepAliveTimeout:600 handler: ^{
NSLog(#"applicationDidEnterBackground:: setKeepAliveTimeout:handler^");//task as you want to do
}];
}
#else
LogInfo(#"applicationDidEnterBackground (Not supported)");
#endif
}
When you get your read and write CFStream socket object after a successful TCP connection, add this code
CFReadStreamSetProperty(yourCFReadStreamObj, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(yourCFWriteStreamObj, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
After flowwing these steps your socket should receive message in background too and connection will be active till the time you give in "applicationDidEnterBackground".
I hope this will help. Thanks
I have a challenge with XMPPFramework today...
I've configured ejabberd correctly: server is up and running and registering users correctly by in-band registration.
But when I try to send a subscription request from one device, the second one can't recieve the presence for the first one. Here's the code I use on the first device to subscribe to a user, as documented:
[[[self appDelegate] xmppRoster] addUser:userJID withNickname:nickname groups:nil subscribeToPresence:YES];
And if everything goes ok, the second device must fire the delegate method:
- (void)xmppRoster:(XMPPRoster *)sender didReceiveBuddyRequest:(XMPPPresence *)presence
The problem is that this delegate method is never fired.
The server creates the new contact for the user, I can see it on ejabberd web administrator, but the subscription type is set to none.
Any ideas?
UPDATE:
Well, it seems that I was forgetting to include the hostname into the JID. Now, the delegates for XMPPRoster are not being called, but I can see on ejabberd that the request is not 'none' but 'both' and pending validation.
Can't imagine why the XMPPRoster delegates are not firing... it seems that ejabberd server don't send the proper notification when a user receives a new subscription request...
It seems that eJabberd was misconfigured or something. Just installed OpenFire and everything works correctly.