28

I'm not sure how to use the XMPPFramework's core data to store incoming messages. Does anyone have any tutorials on how to do this? I see User objects, which in turn can have many "resources". Is each message received supposed to be a new resource that I create and persist?

I do not know what part is my responsibility and what part the framework provides regarding message history. I can intercept every incoming message. Then am I supposed to create and store each message inside a Messages table using core data? I'd have a Message Entity. And each XMPPUser would have an array of Message objects. But then wouldn't I be rolling my own solution, which would be working against the framework?

Thanks!

user798719
  • 9,619
  • 25
  • 84
  • 123

4 Answers4

52

I know this is an old thread but as I am currently working with XMPP on iOS I must say that there is a built in support for archiving messages in XMPP.

I downloaded the XMPP framework for iOS and in it there is folder marked XEP-0136. Import the folders in XCode and activate client side archiving by using the following lines of code in the class you instantiate XMPP client:

xmppMessageArchivingStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
xmppMessageArchivingModule = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:xmppMessageArchivingStorage];

the following one line of code saves you from sending archive specific stanzas to the xmpp server which will most probably respond with service-not-implemented

[xmppMessageArchivingModule setClientSideMessageArchivingOnly:YES];

[xmppMessageArchivingModule activate:xmppStream];
[xmppMessageArchivingModule  addDelegate:self delegateQueue:dispatch_get_main_queue()];

And you are set. From that moment on, messages (outgoing and incoming) will be stored in a table created by the framework.

If you need more info please comment and i will get back to you.

@PraviJay

I did like this :

-(void)testMessageArchiving{
            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 print:[[NSMutableArray alloc]initWithArray:messages]];
}

-(void)print:(NSMutableArray*)messages{
         @autoreleasepool {
            for (XMPPMessageArchiving_Message_CoreDataObject *message in messages) {
                NSLog(@"messageStr param is %@",message.messageStr);
                NSXMLElement *element = [[NSXMLElement alloc] initWithXMLString:message.messageStr error:nil];
                NSLog(@"to param is %@",[element attributeStringValueForName:@"to"]);
                NSLog(@"NSCore object id param is %@",message.objectID);
                NSLog(@"bareJid param is %@",message.bareJid);
                NSLog(@"bareJidStr param is %@",message.bareJidStr);
                NSLog(@"body param is %@",message.body);
                NSLog(@"timestamp param is %@",message.timestamp);
                NSLog(@"outgoing param is %d",[message.outgoing intValue]);
            }
        }
}

Hope it helps :)

Rohit Mandiwal
  • 10,258
  • 5
  • 70
  • 83
Simon
  • 761
  • 6
  • 12
  • 1
    how can fetch that history of messages from xmppMessageArchivingModule – Prabhjot Singh Gogana Feb 28 '13 at 13:07
  • 1
    @PraviJay if you have any issues let me know, maybe i came across them too. – Simon Mar 01 '13 at 11:02
  • 1
    thanx man i just stuked with this line NSArray *messages = [moc executeFetchRequest:request error:&error]; but u did well man.. you are life saver thanks man... – Prabhjot Singh Gogana Mar 01 '13 at 11:30
  • 1
    @PraviJay stackoverflow community (maybe you at sometime) saved me a million times, glad i gave a small piece back. Cheers! – Simon Mar 01 '13 at 12:37
  • dude i need help regarding registration – Prabhjot Singh Gogana Mar 04 '13 at 06:48
  • @PraviJay Registration ? You mean connect to the xmpp server so as to receive and send messages ? – Simon Mar 04 '13 at 13:43
  • I have done the registration (sign up).. have done v-cards i m actually really stuck with it and authorization of add request – Prabhjot Singh Gogana Mar 05 '13 at 05:29
  • @PraviJay Again i am sorry but i don't understand. What do you mean add request ? Please describe what you want to achieve. – Simon Mar 05 '13 at 10:20
  • have u done v-cards or avatars just for save the information regarding users. – Prabhjot Singh Gogana Mar 05 '13 at 10:39
  • @PraviJay No sorry, i have not done that, hope i did not waste your time. – Simon Mar 05 '13 at 10:45
  • ok no problem . thanks for everything. i m on http://chat.stackoverflow.com/rooms/17847/cocos2d – Prabhjot Singh Gogana Mar 05 '13 at 10:54
  • 2
    This is a great snipped of code. I posted below a snippet to load directly into a table view. – deepwinter May 03 '13 at 19:15
  • 1
    Hi, I was using your code snippet to archive message and retrieve the same , but this way its retrieving all the messages. how can I retrieve messages with a particular user only? – Smita May 13 '13 at 20:48
  • @Smita Hi, i am at the moment working on the same thing, but just from the top of my head maybe creating a predicate would help. Something like NSString *predicate = @"jidStr == "; and then adding that to your fetch request. I am not sure the jidstr is the senders id but you get the idea. Test and let me know. [Edit] just tested i think the param you need is bareJid or bareJidStr. – Simon May 14 '13 at 07:23
  • 2
    @Smita Just finished. Go to XMPPMessageArchiving_Message_CoreDataObject and choose the variable on which you want to filter your results. In my case because i have multiple connections the "to" field of the message is different for each message i receive, so i added a "to" attribute. Based on this i added : NSString *predicateFrmt = @"to == %@"; NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFrmt, @"aBareJid"]; request.predicate = predicate; Hope it helps. – Simon May 14 '13 at 08:35
  • hey, thank you sooo much .. that worked like charm:-) i added both from and to attributes and fetching the results using same .. thanks a lot.. you saved my day :-) – Smita May 14 '13 at 09:13
  • This is local archiving, is it possible to fetch messages from server itself, like we get from cache, since user can switch to different devices and may lose messages in other device, any idea how to do this ? – DivineDesert Jul 19 '13 at 07:26
  • @DivineDesert Hi, quoting myself from my initial post: "the following one line of code saves you from sending archive specific stanzas to the xmpp server which will most probably respond with service-not-implemented". Maybe this is no longer the case, but i could not get the server side archiving to work. – Simon Jul 22 '13 at 09:12
  • @DivineDesert You can also check out my question on the matter here http://stackoverflow.com/questions/14279249/retrieving-archived-messages-from-xmpp-server-in-ios – Simon Jul 22 '13 at 09:14
  • @Simon I might have missed something. Where do you download the XMPP framework for iOS? Is it from this place: https://github.com/robbiehanson/XMPPFramework? Thanks – CodeBrew Nov 27 '13 at 16:23
  • @CodePlumber Yes, this is the one. I originally navigated there from http://xmpp.org/xmpp-software/libraries/ – Simon Nov 28 '13 at 18:01
  • Hi @Simon, first of all thanks a lot for such a wonderful code snippet. I implemented your code, it works but the message which i send from my device are stored twice(i.e outgoing messages) but the incoming msgs are stored perfectly. Issue is the outgoing msgs appear twice. – Parvez Belim Apr 17 '14 at 10:04
  • 1
    @ParvezBelim Hi Parvez, without any code i cannot figure out what is wrong with your implementation. Since the archiving of the messages is done through the third party implementation we all use, i suggest you take another look at your code. How do you figure out that the outgoing messages are stored twice ? Did you try to open the sqlite database created by the xmpp and take a look inside ? – Simon Apr 23 '14 at 06:30
  • @Simon Please help me to accept invitation concept for the XMPP MUC ROOM – Sudha Tiwari Jun 25 '14 at 04:54
  • @Smita @Simon Can you specify the to and from predicate. Is it work `NSString *predicateFrmt = @"(to == %@ AND from == %@) OR (to == %@ AND from == %@)";` `NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFrmt, myID, toId, toId, myID];` . As core data has only following fields. `bareJid bareJidStr body thread outgoing isOutgoing composing isComposing timestamp streamBareJidStr`. There is no `to` and `from`. Should we use `@"(bareJidStr == myId AND isOutgoing == 1) OR (bareJidStr == toId AND isOutgoing == 0)"` – Amit Battan Sep 27 '14 at 18:52
  • @AmitBattan Hi Amit, i am assuming what you want to achieve with the predicate is to retrieve a chat between two users. So as it has been long since i did this, i checked the latest version of XMPP for Objective C and indeed "to" in not in core data. But is there in the message ? Use the code above to print the fields of an xmpp message, if you can find the to attribute, add it to your XMPPMessageArchiving_Message_CoreDataObject and form your predicate accordingly. If this doesn't work let me know. – Simon Sep 29 '14 at 06:40
  • getting zero records without ant predicate – Amit Battan Sep 30 '14 at 17:00
  • I explore the respective sqlite there are records in this table. but not getting by this code even without predicate – Amit Battan Sep 30 '14 at 17:37
  • @AmitBattan So the print method does not print anything ? – Simon Oct 01 '14 at 06:57
  • hello @Simon , how we can save chat room messages to core data ? – Sushil Sharma Apr 18 '15 at 06:36
  • @Sushil Hi, i stopped working with XMPP a while back, but as a general rule i would tell you that every message received by the client can be archived (stored in core data). I do not see why chat room messages are different in essence from simple chat messages. – Simon Apr 22 '15 at 12:03
  • Storing chat to core using xmpp or using custom entity. which one will be better? – Bista Apr 24 '15 at 09:48
  • @the_UB Hi, i don't quite understand the question. I mean, xmpp client implementation for iOS has a way to interact with core data and some entities defined for message, contact and probably something else i don't remember. These are two different things, can you be a little more specific on what you want to know ? – Simon Apr 24 '15 at 10:25
  • `XMPPMessageArchivingCoreDataStorage` or `NSManagedObjectContext`, which one is good to use to store chat messages? – Bista Apr 24 '15 at 10:38
  • @the_UB I would say without a doubt XMPPMessageArchivingCoreDataStorage. – Simon Apr 24 '15 at 11:01
  • I would like to ask. why? – Bista Apr 24 '15 at 11:43
  • Because the core data implementation in this library is excellent (with multithreaded logic) and (most importantly) already done by someone else. – Simon Apr 24 '15 at 13:46
  • @Simon...I wanted to ask you,I have did all the things as above. I have all message data stored in slite file but I just want to confirm that is this sqlite file or messages history are stored on xmpp server so that I can fetch it? plz help... – swapnali patil Aug 18 '15 at 11:20
  • This will store messages in the client side. But how can i do this in the server side to avoid message loss when internet connection lost? – pranavjayadev Sep 07 '15 at 04:55
  • @Simon i am not getting any value in this=> NSArray *messages = [moc executeFetchRequest:request error:&error]; – ramesh bhuja Jun 14 '16 at 12:09
  • @PraviJay i face issue in this line=> NSArray *messages = [moc executeFetchRequest:request error:&error]; please help me – ramesh bhuja Jun 14 '16 at 12:39
10

The responses that indicate XMPP Framework doesn't save the history are incorrect.

To integrate results in a table view use:

XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
NSManagedObjectContext *moc = [storage mainThreadManagedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Contact_CoreDataObject"
                                                     inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entityDescription];

_contactsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:nil cacheName:@"MessagesContactListCache"];

NSError *error;
BOOL rval = [_contactsController performFetch:&error];
Nathan Arthur
  • 8,287
  • 7
  • 55
  • 80
deepwinter
  • 4,568
  • 2
  • 31
  • 37
0

an example to get archived messages in Swift 4

declares and initializes the variables XMPPMessageArchivingCoreDataStorage where I initialize the XMPPStream

    var xmppMessageStorage: XMPPMessageArchivingCoreDataStorage?
    var xmppMessageArchiving: XMPPMessageArchiving?

    xmppMessageStorage = XMPPMessageArchivingCoreDataStorage.sharedInstance()
        xmppMessageArchiving = XMPPMessageArchiving(messageArchivingStorage: xmppMessageStorage)

        xmppMessageArchiving?.clientSideMessageArchivingOnly = true
        xmppMessageArchiving?.activate(stream)
        xmppMessageArchiving?.addDelegate(self, delegateQueue: DispatchQueue.main)

doing this, whenever a message arrives, this will cause it to be archived without needing to do anything else.

then, to retrieve the archived message

func RecibedMessageArchiving(idFriend: String) {

        let JabberIDFriend = idFriend   //id friend chat, example test1@example.com


        let moc = xmppMessageStorage?.mainThreadManagedObjectContext
        let entityDescription = NSEntityDescription.entity(forEntityName: "XMPPMessageArchiving_Message_CoreDataObject", in: moc!)
        let request = NSFetchRequest<NSFetchRequestResult>()
        let predicateFormat = "bareJidStr like %@ "
        let predicate = NSPredicate(format: predicateFormat, JabberIDFriend)

        request.predicate = predicate
        request.entity = entityDescription

        //jabberID id del usuario, cliente
        var jabberIDCliente = ""
        if let jabberj = globalChat.value(forKey: "jabberID"){
            jabberIDCliente = jabberj as! String
        }


        do {
            let results = try moc?.fetch(request)

            for message: XMPPMessageArchiving_Message_CoreDataObject? in results as? [XMPPMessageArchiving_Message_CoreDataObject?] ?? [] {

                var element: DDXMLElement!
                do {
                    element = try DDXMLElement(xmlString: (message as AnyObject).messageStr)
                } catch _ {
                    element = nil
                }

                let body: String
                let sender: String
                let date: NSDate
                let isIncomings: Bool
                if message?.body != nil {
                    body = (message?.body)!
                } else {
                    body = ""
                }



                if element.attributeStringValue(forName: "to") == JabberIDFriend {
                    sender = jabberIDCliente
                    isIncomings = false

                } else {
                    sender = "test2@example.com"
                    isIncomings = true

                }


                    var m: [AnyHashable : Any] = [:]
                    m["msg"] = message?.body

                    print("body", message?.body)

                    print("test", element.attributeStringValue(forName: "to"))
                    print("test2", element.attributeStringValue(forName: "body"))


            }
        } catch _ {
            //catch fetch error here
        }

    }
Jose Tovar
  • 149
  • 2
  • 13
-4

XMPPFramework does not store message history,So i suggest to you it is better to use core data.Create a table by taking sender,receiver,message,time as columns .Insert record when send message method calling and receive message method calling...

-(void)saveChatHistory:(NSString *)sender:(NSString*)receiver:(NSString*)message:(NSString*)time
{
    NSManagedObjectContext *context=[[self appDelegate] managedObjectContext];
    NSManagedObject *newContext=[NSEntityDescription insertNewObjectForEntityForName:@"ChatHistory" inManagedObjectContext:context];
    [newContext setValue:sender forKey:@"sender"];
    [newContext setValue:receiver forKey:@"receiver"];
    [newContext setValue:message forKey:@"message"];
    [newContext setValue:time forKey:@"time"];
    NSError *error;
    if(![context save:&error])
    {
        UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:@"Error Occured" message:@"Data is not Stored in Database Try Again" delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil];
        [alertView show];

    }

}

Retrive chat history when specific user selected from tableview.... the fallowing method shows how to retrive chat history...and call this method from didSelectRowAtIndexPath method and pass destination id as parameter

-(void)getChatHistory:(NSString*)jidString1
{


    NSManagedObjectContext *context=[[self appDelegate] managedObjectContext];
    NSEntityDescription *entity=[NSEntityDescription entityForName:@"ChatHistory" inManagedObjectContext:context];
    NSFetchRequest *req=[[NSFetchRequest alloc] init];

    NSPredicate *predicate=[NSPredicate predicateWithFormat:@"receiver=%@",jidString1];
    [req setEntity:entity];
    [req setPredicate:predicate];
    NSManagedObject *matchRecords=nil;
    NSError *error;
    NSArray *objects=[context executeFetchRequest:req error:&error];

    if([objects count]==0)
    {
        UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:@"No Record found" message:@"there is no previous chat history" delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil];
        [alertView show];
    }
    else 
    {
        for(int i=0;i<[objects count];i++)
        {
         matchRecords=[objects objectAtIndex:i ];
         NSLog(@"sender is %@",[matchRecords valueForKey:@"sender"]);
         NSLog(@"reciver is %@",[matchRecords valueForKey:@"receiver"]);
         NSLog(@"messages is %@",[matchRecords valueForKey:@"message"]);
         NSLog(@"time is %@",[matchRecords valueForKey:@"time"]);
       }
     }


}

I hope this is useful to you

Venkat
  • 347
  • 3
  • 13