Last active
October 10, 2018 12:36
-
-
Save Eridana/cf3d3838fe8cd841bcbb to your computer and use it in GitHub Desktop.
iOS XMPPFramework
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// XmppManager.h | |
// | |
// | |
// | |
#import <Foundation/Foundation.h> | |
@protocol XmppChatDelegate <NSObject> | |
- (void)didReceiveMessage:(NSDictionary *)msgDict; | |
- (void)didFailToSendMessage; | |
@end | |
@interface XmppManager : NSObject | |
@property id<XmppChatDelegate> chatDelegate; | |
+ (XmppManager *)sharedInstance; | |
- (void)setupStream; | |
- (void)setupRoom; | |
- (void)sendMessage:(NSString *)messageStr; | |
- (NSArray *)requestOldMessagesForChat; | |
- (void)leaveChatRoom; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// XmppManager.m | |
// | |
// | |
// | |
#import "XmppManager.h" | |
#import "XMPPFramework.h" | |
#import "XMPPRoomCoreDataStorage.h" | |
#import "XMPPRoom.h" | |
#import "AppDelegate.h" | |
#import "User.h" | |
#import "XMPPRoomMemoryStorage.h" | |
#import "XMPPMUC.h" | |
#import "XMPPReconnect.h" | |
#import "DDLog.h" | |
#import "DDTTYLogger.h" | |
#import "NSDate+XMPPDateTimeProfiles.h" | |
#import "XMPPMessageArchivingCoreDataStorage.h" | |
#import "XMPPMessageArchiving.h" | |
#import <JFMinimalNotification.h> | |
#define XMPP_HOSTNAME @"xmpp.aa.bb" | |
#define ROOM_JID @"[email protected]" | |
// Log levels: off, error, warn, info, verbose | |
#if DEBUG | |
static const int ddLogLevel = LOG_LEVEL_VERBOSE; | |
#else | |
static const int ddLogLevel = LOG_LEVEL_INFO; | |
#endif | |
@interface XmppManager() <XMPPStreamDelegate, XMPPRoomDelegate, XMPPReconnectDelegate, UIAlertViewDelegate> | |
{ | |
BOOL roomJoined; | |
BOOL userAuthenticated; | |
int savedMessagesCount; | |
JFMinimalNotification *minimalNotification; | |
} | |
@property XMPPStream *xmppStream; | |
@property XMPPReconnect *xmppReconnect; | |
// saving messages | |
@property XMPPMessageArchivingCoreDataStorage *xmppMessageArchivingStorage; | |
@property XMPPMessageArchiving *xmppMessageArchivingModule; | |
// chat room | |
@property XMPPRoomMemoryStorage *xmppChatRoomStorage; | |
@property XMPPRoom *xmppChatRoom; | |
@property XMPPJID *chatRoomJid; | |
@property NSMutableArray *lastChatMessages; | |
// user data | |
@property XMPPJID *userJid; | |
@property User *currentUser; | |
@property NSString *jidUserName; | |
@end | |
@implementation XmppManager | |
+ (XmppManager *)sharedInstance | |
{ | |
static XmppManager * _sharedInstance = nil; | |
static dispatch_once_t oncePredicate; | |
dispatch_once(&oncePredicate, ^{ | |
_sharedInstance = [[XmppManager alloc] init]; | |
[_sharedInstance setupStream]; | |
}); | |
return _sharedInstance; | |
} | |
- (void)setupStream | |
{ | |
self.currentUser = [AppManagedContext getCurrentUser]; | |
self.jidUserName = [self.currentUser.jid componentsSeparatedByString:@"@"][0]; | |
self.userJid = [XMPPJID jidWithString:self.currentUser.jid]; | |
self.lastChatMessages = [NSMutableArray new]; | |
savedMessagesCount = 0; | |
// uncomment this to enable full logging | |
// [DDLog addLogger:[DDTTYLogger sharedInstance]]; | |
self.xmppStream = [[XMPPStream alloc] init]; | |
[self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; | |
#if !TARGET_IPHONE_SIMULATOR | |
{ | |
self.xmppStream.enableBackgroundingOnSocket = YES; | |
} | |
#endif | |
self.xmppStream.hostName = XMPP_HOSTNAME; | |
self.xmppStream.myJID = self.userJid; | |
self.xmppStream.hostPort = 5222; | |
self.xmppReconnect = [[XMPPReconnect alloc] init]; | |
[self.xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()]; | |
[self.xmppReconnect activate:self.xmppStream]; | |
// uncomment this block to enable built-in message saving | |
/* | |
self.xmppMessageArchivingStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; | |
self.xmppMessageArchivingModule = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:self.xmppMessageArchivingStorage]; | |
[self.xmppMessageArchivingModule setClientSideMessageArchivingOnly:YES]; | |
[self.xmppMessageArchivingModule activate:self.xmppStream]; | |
[self.xmppMessageArchivingModule addDelegate:self delegateQueue:dispatch_get_main_queue()]; | |
*/ | |
[self connect]; | |
} | |
- (void)connect | |
{ | |
[self.xmppStream setMyJID:self.userJid]; | |
NSError *error = nil; | |
if (![self.xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error]) | |
{ | |
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" | |
message:[NSString stringWithFormat:@"Cannot connect to server %@", [error localizedDescription]] | |
delegate:nil | |
cancelButtonTitle:@"OK" | |
otherButtonTitles:nil]; | |
[alertView show]; | |
} | |
} | |
- (void)setupRoom | |
{ | |
if (roomJoined == YES) return; | |
roomJoined = YES; | |
self.chatRoomJid = [XMPPJID jidWithString:ROOM_JID]; | |
self.xmppChatRoomStorage = [[XMPPRoomMemoryStorage alloc] init]; | |
self.xmppChatRoom = [[XMPPRoom alloc] | |
initWithRoomStorage:self.xmppChatRoomStorage | |
jid:self.chatRoomJid | |
dispatchQueue:dispatch_get_main_queue()]; | |
[self.xmppChatRoom activate:[self xmppStream]]; | |
[self.xmppChatRoom addDelegate:self delegateQueue:dispatch_get_main_queue()]; | |
// set room history messages count to 10 | |
NSXMLElement *xmlHistoryCount = [[NSXMLElement alloc] initWithXMLString:@"<history maxstanzas='10'/>" error:nil]; | |
[self.xmppChatRoom joinRoomUsingNickname:self.jidUserName history:xmlHistoryCount]; | |
[self.xmppChatRoom fetchConfigurationForm]; | |
} | |
- (void)leaveChatRoom | |
{ | |
roomJoined = NO; | |
[self.xmppChatRoom leaveRoom]; | |
[self.xmppChatRoom deactivate]; | |
[self.xmppChatRoom removeDelegate:self]; | |
} | |
- (NSArray *)requestOldMessagesForChat | |
{ | |
return [self.lastChatMessages copy]; | |
// uncomment this block to emable built-in message saving | |
/* | |
XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance]; | |
NSManagedObjectContext *moc = [storage mainThreadManagedObjectContext]; | |
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" | |
inManagedObjectContext:moc]; | |
NSFetchRequest *request = [[NSFetchRequest alloc]init]; | |
[request setEntity:entityDescription]; | |
NSError *error; | |
NSArray *messages = [moc executeFetchRequest:request error:&error]; | |
[self parseMessages:[messages mutableCopy]]; | |
*/ | |
} | |
/* | |
// parse saved messages | |
-(void)parseMessages:(NSMutableArray*)messages | |
{ | |
for (XMPPMessageArchiving_Message_CoreDataObject *message in messages) { | |
NSError *error; | |
// here could be an exception sometimes | |
XMPPMessage *xmppMessage = [[XMPPMessage alloc] initWithXMLString:message.messageStr error:&error]; | |
if (error) { | |
NSLog(@"error when parsing messages : %@", [error description]); | |
return; | |
} | |
if ([message.bareJidStr isEqualToString:[self.chatRoomJid full]]) { | |
[self parseMessage:xmppMessage]; | |
} | |
} | |
} | |
*/ | |
- (void)saveMessage:(NSMutableDictionary *)message | |
{ | |
if ([self.lastChatMessages containsObject:message] == NO) { | |
[self.lastChatMessages addObject:message]; | |
savedMessagesCount++; | |
savedMessagesCount = savedMessagesCount % 10; | |
} | |
} | |
- (void)sendMessage:(NSString *)messageStr | |
{ | |
if([messageStr length] == 0 || [self.currentUser.username length] == 0) return; | |
// I have to send message with JSON body (my API required), but usually it's just text | |
/* | |
"chat": { | |
"username": "user123", | |
"type": "message", | |
"time": "2015-04-12T18:51:19+00:00", | |
"text": "Test" | |
} | |
*/ | |
NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; | |
[formatter setTimeZone:[NSTimeZone localTimeZone]]; | |
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; | |
[formatter setLocale:enUSPOSIXLocale]; | |
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; | |
NSDate *now = [NSDate date]; | |
NSString *iso8601String = [formatter stringFromDate:now]; | |
NSString *type = [self.currentUser.isChatModerator boolValue] == YES ? @"moderator_message" : @"message"; | |
NSDictionary *chat = @{ | |
@"username" : self.currentUser.username, | |
@"type" : type, | |
@"time" : iso8601String, | |
@"text" : messageStr | |
}; | |
NSDictionary *json = @{@"chat" : chat}; | |
NSError *error; | |
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&error]; | |
NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; | |
XMPPMessage *message = [[XMPPMessage alloc] init]; | |
[message addAttributeWithName:@"xml:lang" stringValue:@"en"]; | |
NSXMLElement *nick = [NSXMLElement elementWithName:@"nick"]; | |
[nick setXmlns:@"http://jabber.org/protocol/nick"]; | |
[nick setStringValue:self.jidUserName]; | |
NSXMLElement *body = [NSXMLElement elementWithName:@"body"]; | |
[body setStringValue:jsonStr]; | |
[message addChild:body]; | |
[message addChild:nick]; | |
[self.xmppChatRoom sendMessage:message]; | |
} | |
- (NSMutableDictionary *)parseMessage:(XMPPMessage *)message | |
{ | |
NSMutableDictionary *msgDict; | |
if ([message body].length > 0) { | |
// my API send a JSON with data. so I parse JSON here from message body | |
NSError *jsonError; | |
NSData *objectData = [[message body] dataUsingEncoding:NSUTF8StringEncoding]; | |
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:objectData | |
options:NSJSONReadingMutableContainers | |
error:&jsonError]; | |
if (jsonError) { | |
return nil; | |
} | |
NSDictionary *innerDict = [jsonDict objectForKey:@"chat"]; | |
if (innerDict) { | |
NSString *body = [innerDict objectForKey:@"text"]; | |
BOOL myMessage = NO; | |
BOOL privateMessageForMe = NO; | |
NSString *from = [innerDict objectForKey:@"username"]; | |
if ([from isEqualToString:self.currentUser.username]) { | |
from = self.currentUser.username; | |
myMessage = YES; | |
} | |
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; | |
NSString *chatViewTemp = [defaults valueForKey:@"chatTemp"]; | |
if([body hasPrefix:[NSString stringWithFormat:@"@%@",self.currentUser.username]] && [chatViewTemp isEqualToString:@"outchat"]) { | |
NSString *alertBody = [body substringFromIndex:[NSString stringWithFormat:@"@%@",self.currentUser.username].length]; | |
NSString *messagePrivate = [NSString stringWithFormat:@"User %@ had sent you a message: %@", from, alertBody]; | |
NSLog(@"messagePrivate %@", messagePrivate); | |
minimalNotification = [JFMinimalNotification notificationWithStyle:JFMinimalNotificationStyleInfo title:@"Сообщение" subTitle:messagePrivate dismissalDelay:2.0]; | |
minimalNotification.presentFromTop = YES; | |
UIFont* titleFont = [UIFont fontWithName:@"HelveticaNeue" size:18]; | |
[minimalNotification setTitleFont:titleFont]; | |
UIFont* subTitleFont = [UIFont fontWithName:@"HelveticaNeue" size:16]; | |
[minimalNotification setSubTitleFont:subTitleFont]; | |
[App.window.rootViewController.view addSubview:minimalNotification]; | |
[minimalNotification show]; | |
} | |
NSString *type = [innerDict objectForKey:@"type"]; | |
BOOL isAdmin = NO; | |
if ([type isEqualToString:@"moderator_message"]) { | |
isAdmin = YES; | |
} | |
NSString *timeStamp = [innerDict objectForKey:@"time"]; | |
NSDate *date = [NSDate dateWithXmppDateTimeString:timeStamp]; | |
if (!date) { | |
date = [NSDate date]; | |
} | |
// if body contains @nick | |
if([body hasPrefix:[NSString stringWithFormat:@"@"]]) { | |
if ([body hasPrefix:[NSString stringWithFormat:@"@%@",self.currentUser.username]]) { | |
privateMessageForMe = YES; | |
NSLog(@"hi my private message from my friend"); | |
msgDict = [[NSMutableDictionary alloc] init]; | |
[msgDict setObject:body forKey:@"msg"]; | |
[msgDict setObject:from forKey:@"sender"]; | |
[msgDict setObject:[NSNumber numberWithBool:isAdmin] forKey:@"isAdmin"]; | |
[msgDict setObject:date forKey:@"time"]; | |
[msgDict setObject:[NSNumber numberWithBool:myMessage] forKey:@"isMine"]; | |
[msgDict setObject:[NSNumber numberWithBool:privateMessageForMe] forKey:@"privateMessage"]; | |
} else if ([from isEqualToString:self.currentUser.username]) { | |
privateMessageForMe = YES; | |
NSLog(@"hi private message from me!"); | |
msgDict = [[NSMutableDictionary alloc] init]; | |
[msgDict setObject:body forKey:@"msg"]; | |
[msgDict setObject:from forKey:@"sender"]; | |
[msgDict setObject:[NSNumber numberWithBool:isAdmin] forKey:@"isAdmin"]; | |
[msgDict setObject:date forKey:@"time"]; | |
[msgDict setObject:[NSNumber numberWithBool:myMessage] forKey:@"isMine"]; | |
[msgDict setObject:[NSNumber numberWithBool:privateMessageForMe] forKey:@"privateMessage"]; | |
} else { | |
msgDict = [[NSMutableDictionary alloc] init]; | |
[msgDict setObject:@"Blocked" forKey:@"msg"]; | |
[msgDict setObject:@"Blocked" forKey:@"sender"]; | |
[msgDict setObject:[NSNumber numberWithBool:isAdmin] forKey:@"isAdmin"]; | |
[msgDict setObject:date forKey:@"time"]; | |
[msgDict setObject:[NSNumber numberWithBool:myMessage] forKey:@"isMine"]; | |
[msgDict setObject:[NSNumber numberWithBool:privateMessageForMe] forKey:@"privateMessage"]; | |
} | |
} else { | |
msgDict = [[NSMutableDictionary alloc] init]; | |
[msgDict setObject:body forKey:@"msg"]; | |
[msgDict setObject:from forKey:@"sender"]; | |
[msgDict setObject:date forKey:@"time"]; | |
} | |
} | |
return msgDict; | |
} | |
#pragma mark - Delegate Methods | |
#pragma mark - XMPPStreamDelegate | |
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence | |
{ | |
NSLog(@"xmppStream didReceivePresence"); | |
} | |
- (void)xmppStreamDidConnect:(XMPPStream *)sender | |
{ | |
NSLog(@"user connected"); | |
if(sender == self.xmppStream) { | |
NSError *error = nil; | |
if (![[self xmppStream] authenticateWithPassword:self.currentUser.jid_password error:&error]) { | |
NSLog(@"Error authenticating: %@", error); | |
} | |
else { | |
if (self.xmppChatRoom && self.xmppChatRoom.isJoined == NO) { | |
// reconnect to existing room (if wi-fi or cell was turned off/on) | |
NSXMLElement *xmlHistoryCount = [[NSXMLElement alloc] initWithXMLString:@"<history maxstanzas='10'/>" error:nil]; | |
[self.xmppChatRoom joinRoomUsingNickname:self.jidUserName history:xmlHistoryCount password:self.currentUser.jid_password]; | |
} | |
} | |
} | |
} | |
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender | |
{ | |
NSLog(@"user authenticated"); | |
XMPPPresence *presence = [XMPPPresence presence]; // type="available" is implicit | |
[self.xmppStream sendElement:presence]; | |
userAuthenticated = YES; | |
[self setupRoom]; | |
} | |
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message | |
{ | |
} | |
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex | |
{ | |
} | |
- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message | |
{ | |
NSLog(@"message did send from %@", sender.hostName); | |
} | |
- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error | |
{ | |
NSLog(@"xmppStream didFailToSendMessage %@", [error description]); | |
if (self.chatDelegate && [self.chatDelegate respondsToSelector:@selector(didFailToSendMessage)]) { | |
[self.chatDelegate didFailToSendMessage]; | |
} | |
} | |
#pragma mark - XMPPReconnectDelegate | |
- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkReachabilityFlags)connectionFlags | |
{ | |
NSLog(@"didDetectAccidentalDisconnect:%u",connectionFlags); | |
[self leaveChatRoom]; | |
} | |
- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkReachabilityFlags)reachabilityFlags | |
{ | |
NSLog(@"shouldAttemptAutoReconnect:%u",reachabilityFlags); | |
return YES; | |
} | |
#pragma mark - XMPPRoomDelegate | |
- (void)xmppRoom:(XMPPRoom *)sender occupantDidJoin:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence | |
{ | |
} | |
- (void)xmppRoom:(XMPPRoom *)sender occupantDidUpdate:(XMPPJID *)occupantJID withPresence:(XMPPPresence *)presence | |
{ | |
} | |
- (void)xmppRoom:(XMPPRoom *)sender didReceiveMessage:(XMPPMessage *)message fromOccupant:(XMPPJID *)occupantJID | |
{ | |
NSLog(@"xmppRoom didReceiveMessage %@",message); | |
if ([sender isEqual:self.xmppChatRoom]) { | |
NSMutableDictionary *msgDict = [self parseMessage:message]; | |
[self saveMessage:msgDict]; | |
if (self.chatDelegate && [self.chatDelegate respondsToSelector:@selector(didReceiveMessage:)]) { | |
[self.chatDelegate didReceiveMessage:msgDict]; | |
} | |
} | |
} | |
- (void)xmppRoom:(XMPPRoom *)sender didFetchConfigurationForm:(NSXMLElement *)configForm | |
{ | |
NSXMLElement *newConfig = [configForm copy]; | |
NSLog(@"BEFORE Config for the room %@",newConfig); | |
NSArray *fields = [newConfig elementsForName:@"field"]; | |
for (NSXMLElement *field in fields) | |
{ | |
NSString *var = [field attributeStringValueForName:@"var"]; | |
// Make Room Persistent | |
if ([var isEqualToString:@"muc#roomconfig_persistentroom"]) { | |
[field removeChildAtIndex:0]; | |
[field addChild:[NSXMLElement elementWithName:@"value" stringValue:@"1"]]; | |
} | |
} | |
[sender configureRoomUsingOptions:newConfig]; | |
} | |
- (void)xmppRoomDidCreate:(XMPPRoom *)sender{ | |
} | |
- (void)xmppRoomDidJoin:(XMPPRoom *)sender { | |
NSLog(@"xmppRoomDidJoin"); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment