2

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;

  }
Mickaël Rémond
  • 9,035
  • 1
  • 24
  • 44
iYousafzai
  • 1,021
  • 1
  • 10
  • 29

2 Answers2

1

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"

Mickaël Rémond
  • 9,035
  • 1
  • 24
  • 44
  • You mean setting it like this :[_xmppStream setMyJID:[XMPPJID jidWithString:jabberID]]; jabberID = user@domain/udid and when user logs out and another user lets say user 2 logs in then it should be like jabberID = user2@domain/udid udid :Generated on first install. – iYousafzai Jan 27 '16 at 11:33
  • I do not recommend to use udid. Just generate a short random string on first install, like 6 to 8 chars long. Something like: "RX2eE7" for example. Udid are too long and will consume significantly more bandwidth. – Mickaël Rémond Jan 27 '16 at 11:42
  • Yes i will be generating a random number.Going to test and will get back to you.Thanks. – iYousafzai Jan 27 '16 at 11:46
  • oh man.Looks like the delay and duplication issues are gone.Still some bugs: 1) sometimes i can see the user online even though i have closed the session and disconnected. 2)Push notifications are received when user logs out.may be because of this (NSXMLElement *offline = [NSXMLElement elementWithName:@"offline" stringValue:@"true"];). – iYousafzai Jan 27 '16 at 12:31
  • Still facing the issues with push notifications (app in background with logged in user) @Mickaël Rémond – iYousafzai Jan 27 '16 at 16:36
  • 1) sometimes i can see the user online even though i have closed the session and disconnected. -> With push if you want to get offline you have to close the session. Send presence unavailable and / or explicitely close the stream (Standard XMPP). Just closing the connection keep the session alive. 2) Indeed, if you want offline message, you receive them when session is over. If you want to remove user account from device, unregister from push. – Mickaël Rémond Jan 28 '16 at 06:35
  • "Still facing the issues with push notifications (app in background with logged in user)" Well, describe the issue on a new Stackoverflow issue. This one was address and replied. – Mickaël Rémond Jan 28 '16 at 06:35
  • Thank you so much.Here is the new issue. http://stackoverflow.com/questions/35056452/xmpp-ejabberd-saas-push-notifications – iYousafzai Jan 28 '16 at 08:37
0

you should notify stream management to disconnect your session and call this method for disconnect in tearDown :

    [self.stream disconnectAfterSending];
Mo Farhand
  • 1,144
  • 8
  • 21