XMPP push notifications causing problems (delay + duplications) in messages - xmpp

XMPP push notifications causing problems (delay + duplications) in messages.
I have successfully created a chat application using XMPP + Ejabberd.
Without Push Notifications:
Both single and group chat messages are working perfectly.
With Push Notifications:
Sometimes everything works perfectly.Notifications are triggered and messages are received with out any delay or duplications.
Sometimes no notifications are triggered (while app in background) but messages are received perfectly.
Sometimes notifications are triggered but messages are received with delay and duplications.
Everything on the sever side is configured correctly.They advised to fix your issues by making sure each session connects with one persistent resource, making connection stable using whitespace keep alive and when connection is lost just rebinding with same resource.
I have stream management,xmppStream.enableBackgroundingOnSocket and App provides Voice over IP services background mode enabled.
When user logs out or app is terminated i teardown the stream and send an unavailable presence.
Below is my code for xmpp stream push notifications and connect/disconnect.
I am pulling out my hair over this.if you guys have any idea please let me know.
Thanks.
#pragma mark - Connect/Disconnect
- (BOOL)connect {
if (!_xmppStream) {
NSLog(#"Setting up Stream");
[self setupStream];
}
if (![_xmppStream isDisconnected]) {
return YES;
}
NSString *jabberID = [[NSUserDefaults standardUserDefaults] stringForKey:#"userID"];
NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey:#"userPassword"];
if (jabberID == nil || myPassword == nil) {
return NO;
}
[_xmppStream setMyJID:[XMPPJID jidWithString:jabberID]];
_password = myPassword;
NSError *error = nil;
if (![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error]){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[NSString stringWithFormat:#"Can't connect to server! %#", [error localizedDescription]] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
return NO;
}
return YES;
}
- (void)disconnect {
[self goOffline];
[self teardownStream];
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
[self goOnline];
//Stream Management
NSXMLElement *enable = [NSXMLElement elementWithName:#"enable" xmlns:#"urn:xmpp:sm:3"];
[enable addAttributeWithName:#"resume" stringValue:#"true"];
[_xsm.xmppStream sendElement:enable];
//Push
[self configurePushNotifications];
//
}
-(void)configurePushNotifications{
NSString *jabberID = [[NSUserDefaults standardUserDefaults] stringForKey:#"userID"];
NSXMLElement *iq = [NSXMLElement elementWithName:#"iq"];
[iq addAttributeWithName:#"type" stringValue:#"set"];
[iq addAttributeWithName:#"id" stringValue:idString];
NSXMLElement *push = [NSXMLElement elementWithName:#"push" xmlns:#"p1:push"];
NSXMLElement *keepalive = [NSXMLElement elementWithName:#"keepalive"];
[keepalive addAttributeWithName:#"max" integerValue:30];
NSXMLElement *session = [NSXMLElement elementWithName:#"session"];
[session addAttributeWithName:#"duration" integerValue:60];
NSXMLElement *body = [NSXMLElement elementWithName:#"body"];
[body addAttributeWithName:#"send" stringValue:#"all"];
[body addAttributeWithName:#"groupchat" stringValue:#"true"];
[body addAttributeWithName:#"from" stringValue:jabberID];
NSXMLElement *status = [NSXMLElement elementWithName:#"status"];
[status addAttributeWithName:#"type" stringValue:[NSString stringWithFormat:#"New message from %#",jabberID]];
NSXMLElement *offline = [NSXMLElement elementWithName:#"offline" stringValue:#"true"];
[push addChild:keepalive];
[push addChild:session];
[push addChild:body];
[push addChild:status];
[push addChild:offline];
NSXMLElement *notification = [NSXMLElement elementWithName:#"notification"];
[notification addChild:[NSXMLElement elementWithName:#"type" stringValue:#"applepush"]];
[notification addChild:[NSXMLElement elementWithName:#"id" stringValue:_userDeviceToken]];
[push addChild:notification];
NSXMLElement *appid = [NSXMLElement elementWithName:#"appid" stringValue:#"appid"];
[push addChild:appid];
[iq addChild:push];
[[self xmppStream] sendElement:iq];
}
- (void)setupStream {
_xmppStream = [[XMPPStream alloc] init];
_xmppStream.hostName = kHostName;
_xmppStream.hostPort = kHostPort;
_xmppStream.enableBackgroundingOnSocket = YES;
[_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
//XMPPReconnect
_xmppReconnect = [[XMPPReconnect alloc] init];
[_xmppReconnect activate:_xmppStream];
//Stream Management
_xsm = [[XMPPStreamManagement alloc] init];
[_xsm enableStreamManagementWithResumption:YES maxTimeout:0];
[_xsm activate:_xmppStream];
//Last Activity
_xmppLastActivity = [[XMPPLastActivity alloc] initWithDispatchQueue:dispatch_get_main_queue()];
[_xmppLastActivity addDelegate:self delegateQueue:dispatch_get_main_queue()];
[_xmppLastActivity activate:_xmppStream];
}
- (void)goOnline {
XMPPPresence *presence = [XMPPPresence presence];
[[self xmppStream] sendElement:presence];
}
- (void)goOffline {
XMPPPresence *presence = [XMPPPresence presenceWithType:#"unavailable"];
[[self xmppStream] sendElement:presence];
}
- (void)teardownStream {
[_xmppStream disconnect];
[_xmppStream removeDelegate:self];
[_xmppReconnect removeDelegate:self];
[_xmppLastActivity removeDelegate:self];
[_xmppReconnect deactivate];
_xmppStream = nil;
_xmppReconnect = nil;
_xmppLastActivity = nil;
}

You need to make sure that you are passing the resource when you connect to ejabberd. The resource should be randomly generated on first app install and on subsequent login, you should always use the same resource. Otherwise you are created a new long running detached session on each new login on the server and causing messages to be routed to all pending sessions. When those expires they are routed again etc.
In XMPP, the resource is the identifier of the device basically. You need to generate the JID for login with a string of form "user#domain/resource"

you should notify stream management to disconnect your session and
call this method for disconnect in tearDown :
[self.stream disconnectAfterSending];

Related

IOS Chat application using XMPP Protocol (ejabberd) Room chat issue

I am developing an IOS Chat application using XMPP Protocol(ejabberd). My chat room is created at my server, it return roomID to me.
I am facing an issue in room/group chat. When i am sending a single message it is repeating more than once like 3 to 4 times.How to fix this. My code is here
XMPPJID *roomJID = [XMPPJID jidWithString:[roomDict objectForKey:KEY_group_id]];
XMPPRoom *xmppRoom = [[XMPPRoom alloc] initWithRoomStorage:xmppRoomCoreDataStorage jid:roomJID dispatchQueue:dispatch_get_main_queue()];
[xmppRoom activate:[ChatHandler sharedInstance].xmppStream];
[xmppRoom addDelegate:self
delegateQueue:dispatch_get_main_queue()];
[self insertRoomObjectWithDictionary:roomDict];
[xmppRoom joinRoomUsingNickname:[[ChatHandler sharedInstance] xmppStream].myJID.user
history:nil
password:#""];
[xmppRoom fetchConfigurationForm];
return xmppRoom;
Following code snippet worked for me.. Try it for your code...
I have sent uuid of my device as child while sending message and checked the same uuid at the time of incoming message :
-(void)sendMessageWithBody:(NSString *)messageBody
{
if ([messageBody length] == 0) return;
NSXMLElement *body = [NSXMLElement elementWithName:#"body" stringValue:messageBody];
XMPPMessage *message = [XMPPMessage message];
[message addChild:body];
NSString *uuidString=[UIDevice currentDevice].identifierForVendor.UUIDString;
NSXMLElement *myMsgLogic=[NSXMLElement elementWithName:#"myMsgLogic" stringValue:uuidString];
[message addChild:myMsgLogic];
[self sendMessage:message];
}
-(void)xmppRoom:(XMPPRoom *)sender didReceiveMessage:(XMPPMessage *)message fromOccupant:(XMPPJID *)occupantJID;
{
[self handleIncomingMessage:message room:xmppRoom];
}
-(void)handleIncomingMessage:(XMPPMessage *)message room:(XMPPRoom *)room
{
NSString *uuidString=[UIDevice currentDevice].identifierForVendor.UUIDString;
NSString *messageLogic= [[message elementsForName:#"myMsgLogic"].firstObject stringValue];
if ([uuidString isEqualToString:messageLogic]) {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"handleIncomingMessage" message:[message body] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
}
}
I have the same chat application using xmpp ejabbered. I was also facing the same problem. In my case on xmpp server they have set offline message storage to 100. If in offline mode the message limit crossed 100 then from 101th message I will get bounce back that message. So as a solution we changed the offline message limit to 500.
{mod_offline_odbc, [
{user_max_messages, 500}
]}

XMPPFarmework How to obtain XMPP chat room list in iOS?

XMPPFarmework us openfire
How to obtain XMPP chat room list in iOS ?
Anyone please show me a way to solve out this.Thanks!
Below code works for us when connected to an ejabberd server on Amazon EC2 and hope it should work for Openfire too.
- (void) getChatRooms:(XMPPStream *)stream
{
NSString* server = #"conference.myserver.com";
XMPPJID *servrJID = [XMPPJID jidWithString:server];
XMPPIQ *iq = [XMPPIQ iqWithType:#"get" to:servrJID];
[iq addAttributeWithName:#"id" stringValue:#"chatroom_list"];
[iq addAttributeWithName:#"from" stringValue:[stream myJID].full];
NSXMLElement *query = [NSXMLElement elementWithName:#"query"];
[query addAttributeWithName:#"xmlns" stringValue:#"http://jabber.org/protocol/disco#items"];
[iq addChild:query];
[firstStream sendElement:iq];
}
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
DDLogVerbose(#"%#", [iq description]);
}

typing indication in xmpp chat in iphone

I am developing chat application using XMPP. Here my problem is, I want to send chat notifications .I tried the fallowing way but not getting correctly.
- (void)sendMessage:(id)sender
{
NSString *messageStr =messageField.text;
if([messageStr length] > 0)
{
NSXMLElement *body = [NSXMLElement elementWithName:#"body"];
NSXMLElement *chatStatus=[NSXMLElement elementWithName:#"composing" xmlns:xmlns_chatstates];
[body setStringValue:messageStr];
NSXMLElement *message = [NSXMLElement elementWithName:#"message"];
[message addAttributeWithName:#"type" stringValue:#"chat"];
[message addAttributeWithName:#"to" stringValue:jidString];
[message addChild:chatStatus];
[message addChild:body];
[[self xmppStream] sendElement:message];
}
}
I am using above method for sending a message.But it always shows typing notification even i am not typing .can any one help to me.
thanks in advance
According to XEP-0085: Chat State Notifications, you should send another notification saying you've stopped typing, for example <active/> or <paused/>.

XMPPFramework not sending presence

Im developing an app for iPhone, in wich one of the functionalities is an instant message system, using XMPPFramework. By now, im testing it with Google Talk. The delegate is the same class that manages the User Interface. So, I got this code:
In viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupStream];
}
The setupStream method:
- (void) setupStream
{
NSLog(#"Inside setupStream");
xmppStream = [[XMPPStream alloc] init];
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[self connect];
}
The connect method:
- (BOOL) connect
{
NSLog(#"Inside connect method");
General *general = [General sharedManager];//this is a singleton to manage settings for every user
NSString *chatid;
NSString *chatpass;
//chatid=[general user];
chatid=#"somegmailaccount#gmail.com";
xmppStream.myJID=[XMPPJID jidWithString:chatid];
if (![xmppStream isDisconnected]) {
return YES;
}
NSError *error = nil;
if (![xmppStream connect:&error])
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:[NSString stringWithFormat:#"Can't connect to server %#", [error localizedDescription]]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
return NO;
}
return YES;
}
In xmppStreamDidConnect method:
- (void) xmppStreamDidConnect:(XMPPStream *)sender
{
[xmppStream authenticateWithPassword:#"password" error:NULL];
[self goOnline];
}
And goOnline method:
- (void) goOnline
{
XMPPPresence *presence = [XMPPPresence presence];
[[self xmppStream] sendElement:presence];
NSLog(#"Presence sent");
}
With this, the presence is not sent. I have another google account that I try for testing (say, testing#gmail.com) and in this account the presence of somegmailaccount.gmail.com is not seen. Both accounts are connected and know each other, since I used this same accounts to develop the Android app.
Any idea about what i´m doing wrong? Any help is appreciated.
Thank you very much.
I found it! The presence is not sent this way:
XMPPPresence *presence = [XMPPPresence presence];
[[self xmppStream] sendElement:presence];
Insted, i have done it this way:
NSXMLElement *presence = [NSXMLElement elementWithName:#"presence"];
[xmppStream sendElement:presence];
This way, the presence is sent without any problem :)
With this code, i implement method xmppStreamDidAuthenticate:(XMPPStream *)sender
and the program DO enter that method without any call from my code. But if i put
[xmppStream setHostName:#"talk.google.com"];
[xmppStream setHostPort:5222];
... in method connect, the program DO NOT enter that method, nor xmppStreamDidConnect.
Im getting mad.

PushMeBaby does not work !

I am trying to send push notification via PushMeBaby app according to [this tutorial ,][1] but I don't know why it does not work ! I install iPusher App from itunes and worked fine ! here is my code :
- (id)init {
self = [super init];
if(self != nil) {
self.deviceToken = #"5ce090e5 78d38a8a 149dbe46 cbe87e2e dc6c6d2a 4b97e3b7 a5d3f4c2 b09faad2";
self.payload = #"{\"aps\":{\"alert\":\"You got a new message!\",\"badge\":5,\"sound\":\"beep.wav\"},\"acme1\":\"bar\",\"acme2\":42}";
self.certificate = [[NSBundle mainBundle]
pathForResource:#"aps_developer_identity" ofType:#"cer"];
}
return self;
}
URBan AirShip code :
(void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
// TODO: Pass the token to our server
// Convert the token to a hex string and make sure it's all caps
NSMutableString *tokenString = [NSMutableString stringWithString:[[deviceToken description] uppercaseString]];
[tokenString replaceOccurrencesOfString:#"<" withString:#"" options:0 range:NSMakeRange(0, tokenString.length)];
[tokenString replaceOccurrencesOfString:#">" withString:#"" options:0 range:NSMakeRange(0, tokenString.length)];
[tokenString replaceOccurrencesOfString:#" " withString:#"" options:0 range:NSMakeRange(0, tokenString.length)];
// Create the NSURL for the request
NSString *urlFormat = #"https://go.urbanairship.com/api/device_tokens/%#";
NSURL *registrationURL = [NSURL URLWithString:[NSString stringWithFormat:
urlFormat, tokenString]];
// Create the registration request
NSMutableURLRequest *registrationRequest = [[NSMutableURLRequest alloc]
initWithURL:registrationURL];
[registrationRequest setHTTPMethod:#"PUT"];
// And fire it off
NSURLConnection *connection = [NSURLConnection connectionWithRequest:registrationRequest
delegate:self];
[connection start];
NSLog(#"We successfully registered for push notifications");
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
// Inform the user that registration failed
NSString* failureMessage = #"There was an error while trying to / register for push notifications.";
UIAlertView* failureAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:failureMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[failureAlert show];
[failureAlert release];
}
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// Check for previous failures
if ([challenge previousFailureCount] > 0)
{
// We've already tried - something is wrong with our credentials
NSLog(#"Urban Airship credentials invalid");
return;
}
// Send our Urban Airship credentials
NSURLCredential *airshipCredentials = [NSURLCredential credentialWithUser:#"<GY__T8X4Rg6onkJSO8o0Bg>"
password:#"<Z_fhEasrQ6emwFcWMyiKrA>"
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:airshipCredentials
forAuthenticationChallenge:challenge];
}
I have read through the tutorial you've followed on mobiforge and it is wrong on so many levels!
If you want to use Push Notifications, then there really is no better way than to use Urban Airship
Here are a pair of tutorials which will get you up and running using Urban Airship: Part 1 & Part 2 (I can also verify these work properly as I have followed them many times)
If you would like avoid using a third party and instead have access to your own PHP capable server then here is another pair of tutorials which will help you get setup: Part 1 & Part 2