Storing messages using XMPPFramework for ios? - iphone

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!

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 :)

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];

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
}
}

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

Related

Core data:- How to update values in the array (Transformable)

I work on app that use Core data to save data in local device. In Core data i have save data in array using Transformable format but, i don't know how to update particular values in the array.
My code for update Array is here
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *user = [NSEntityDescription insertNewObjectForEntityForName:#"Type" inManagedObjectContext:context];
NSError *error = nil;
//Set up to get the thing you want to update
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"businessTypes == %#", #"Others"];
[request setPredicate:predicate];
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
} else {
// At least one object saved. There should be only one
// so use the first...
user = [results lastObject];
[user setValue:#"Management" forKey:#"businessTypes"];
}
}
if (![self.managedObjectContext save:&error]) {
//Handle any error with the saving of the context
}
else{
[app saveContext];
NSLog(#"update value successfully");
}
and below is my save array in core data:
{
businessTypes = (
"Social Bussiness",
Marketing,
Transports,
Others
);
},
so i want to update "Others" to "Management" in the array.
When i run this code i have no error but i don't update particular value at index array.
thanks to help me.
Perhaps you are confusing your entities. You fetch an entity called Type but you are calling the object user, indicating that perhaps you wanted to fetch a user that has a certain business type.
If each user has only one "business type", you do not need a Type entity, just a string attribute for the User entity.
If each user can have more than one business type, you should have an entity Type with a name attribute that includes one term indicating the business type, and it should be modeled as a many-to-many relationship.
User <<--------->> Type
To set all types that are now called "Other" to "Management", you would fetch the Type with name "Other", change it and save. To only change one of a user's business types from "Other" to "Management", you would: fetch the user, remove the "Other" type, fetch the "Management" type, add it to the user and save.
If your businessTypes attribute is supposed to be a transformable array with hard-coded strings, you should probably change the data model as described above. You will have much more flexibility and power for searching and handling the data with a clean Core Data model.
You have to modify your update function like this code then you will get your required output
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Type"inManagedObjectContext:context];
[request setEntity:entity];
request.propertiesToFetch= #[ #"businessTypes"];
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
} else {
int loopCount = (int)results.count;
Type* entityType = nil;
for (int index=0; index<loopCount; index++) {
entityType = (Type*)results[index];
if (entityType.businessTypes!=nil) {
NSUInteger reqIndex = [entityType.businessTypes indexOfObject:#"Others"];
[entityType.businessTypes replaceObjectAtIndex:reqIndex withObject:#"Management"];
[entityType setValue:entityType.businessTypes forKey:#"businessTypes"];
}
}
}
if (![self.managedObjectContext save:&error]) {
//Handle any error with the saving of the context
}
else{
[app saveContext];
NSLog(#"update value successfully");
}

Coredata error setObjectForKey: object cannot be nil

I am tryng to check if there is any data in my coredata storage as a type of recovery for my app. Basicly if the user is in the final view there is some data in coredata that they are constantly updating.
So they are in the final view then the app breaks or they put it to sleep then the app gets removed from memory.
when the app is next loaded I check my coredata object to see if there are any values If there are I prompt the user telling them there is unfinished work would you like to pick up from where you left off of continue fresh.
if they want to start fresh i dump anything thats currently in my core data and allow them to work. else I jump to the last view load up the data thats been in coredata and allow them to continue working.
However this is where the error happens I check my coredata like so.
NSMutableArray *checkFinMutableArray = [coreDataController readFin];
if ([checkFinMutableArray count] > 0) {
//Show mesage, recover or not?
UIAlertView *alert = [[UIAlertView alloc] init];
[alert setTitle:#"Selected projects avalible"];
[alert setMessage:#"It appears that you have unfinished projects from a previous session. Would you like to continue working on these projects?"];
[alert setDelegate:self];
[alert addButtonWithTitle:#"Yes"];
[alert addButtonWithTitle:#"No"];
[alert show];
}
this is what my coredata object looks like
- (NSMutableArray *)readFinDimensions {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *projectDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ProjectList *projectList in fetchedObjects) {
NSMutableDictionary *tempProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempProjectDictionaryArray setObject:project.proj forKey:#"Proj"]; // this is where the ap dies
[tempProjectDictionaryArray setObject:project.desc forKey:#"Desc"];
[projectDictionaryArray addObject:tempProjectDictionaryArray];
}
return projectDictionaryArray;
}
and this is what the error looks like
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: object cannot be nil (key: Proj)'
any help would be greatly appreciated.
May be your code must be:
for (ProjectList *projectList in fetchedObjects) {
NSMutableDictionary *tempProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempProjectDictionaryArray setObject:projectList.proj forKey:#"Proj"]; // this is where the ap dies
[tempProjectDictionaryArray setObject:projectList.desc forKey:#"Desc"];
[projectDictionaryArray addObject:tempProjectDictionaryArray];
}
where project.proj is changed by projectList.proj (also for .desc).
That means that project.proj is nil at
[tempProjectDictionaryArray setObject:project.proj forKey:#"Proj"];
An NS(Mutable)Dictionary cannot store nil as a value, so you should check e.g.
if (project.proj != nil) {
[tempProjectDictionaryArray setObject:project.proj forKey:#"Proj"];
}
Alternatively, you can use
tempProjectDictionaryArray = [project dictionaryWithValuesForKeys:#[#"Proj", #"Desc"]];
which does almost the same as
[tempProjectDictionaryArray setObject:project.proj forKey:#"Proj"];
[tempProjectDictionaryArray setObject:project.desc forKey:#"Desc"];
but replaces nil values by [NSNull null], so you can remove these
from the dictionary with
NSArray *keysForNullValues = [tempProjectDictionaryArray allKeysForObject:[NSNull null]];
[tempProjectDictionaryArray removeObjectsForKeys:keysForNullValues];

Core Data Relationships fetch

Ok, I thought I had this but I am not getting the results that I am expecting. Hopefully someone can help.
I have two entities Person and Timesheet with one attribute to-many relationship:
Person.timesheet<--->>Timesheet.user.
The code below works but when I try to add a second timesheet entry it seems to override the first?
I have looked at the Apple Docs and they are a little vague on this subject.
//Add
NSManagedObjectContext *context = self.managedObjectContext;
Person *personAdded = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
Timesheet *timesheet = [NSEntityDescription insertNewObjectForEntityForName:#"Timesheet" inManagedObjectContext:context];;
timesheet.time = #"10:00 Friday";
timesheet.timestamp = [NSDate date];
NSSet *timesheetSet = [NSSet setWithObject:timesheet];
personAdded.name = #"Darren";
personAdded.job = #"Job to be Done";
personAdded.timesheet = timesheetSet;
NSError *error = nil;
[context save:&error];
if (error) {
NSLog(#"[ERROR] COREDATA: Save raised an error - '%#'", [error description]);
}
NSLog(#"[SUCCESS] COREDATA: Inserted new User to database!");
// Load
NSEntityDescription *personEntity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity: personEntity];
error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (!results || error) {
NSLog(#"[ERROR] COREDATA: Fetch request raised an error - '%#'", [error description]);
[request release];
}
NSLog(#"Results: %#",results);
Person *firstUser = [results objectAtIndex:0];
NSLog(#"First User's name: %#",firstUser.name);
NSLog(#"First User's time %#",[[firstUser.timesheet anyObject] valueForKeyPath:#"timestamp"]);
I am wondering if it could be because I am actually setting the Person.timesheet key with the NSSet and not the actual Table? OR could it be that I am not calling the results correctly?
Thanks,
Darren
You should have a generated "CoreDataGeneratedAccessors" method in your Person.h file which gives you a method
[personAdded addTimesheet:timesheetSet];
which creates the links for you ?
Core Data normally generates an add and a remove method for any relationships you define.
Post your person.h file if this is not clear.

How to search thru Core Data entities and delete certain objects

I'm unclear how to remove certain objects from Core Data database. I think I've got it working so I can find the objects, but don't know how to delete them from Core Data. In this example I'm searching the entity News for items which have expired. I use the 'expires' property (an int 32 unix time stamp) and see if the number is less than the current unix time stamp. Not sure if the NSPredicate is right in this.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"News" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
// Set up predicate here?
NSPredicate *pred = [NSPredicate predicateWithFormat:#"expires < %i", dateInt]; // dateInt is a unix time stamp for the current time
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"forename" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error;
NSMutableArray *mutableFetchResults = [[[managedObjectContext executeFetchRequest:request error:&error] mutableCopy] autorelease];
[request release];
// delete the found objects here?
Call -[NSManagedObjectContext deleteObject:] for every object you want to delete, then commit the changes.
You can use NSBatchDeleteRequest available on iOS 9.0+, macOS 10.11+, tvOS 9.0+, watchOS 2.0+
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"expires < %i", dateInt];
NSFetchRequest *fetchRequest = [News fetchRequest];
[fetchRequest setPredicate:predicate];
// Create batch delete request
NSBatchDeleteRequest *deleteReq = [[NSBatchDeleteRequest alloc] initWithFetchRequest:fetchRequest];
NSError *error = nil;
NSBatchDeleteResult *deletedResult = [appDelegate.persistentContainer.viewContext executeRequest:deleteReq error:&error];
if (error) {
NSLog(#"Unable to delete the data");
}
else {
NSLog(#"%# deleted", deleteReq.result);
}
Swift code (from the above link)
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Employee")
fetch.predicate = NSPredicate(format: "terminationDate < %#", NSDate())
let request = NSBatchDeleteRequest(fetchRequest: fetch)
do {
let result = try moc.execute(request)
} catch {
fatalError("Failed to execute request: \(error)")
}
NOTE:
I found below comment about execute of moc
Method to pass a request to the store without affecting the contents of the managed object context.
Which means any unsaved data in moc won't be affected. i.e. if you've created/updated entity that falls in the delete request criteria and don't called save on moc then that object won't be deleted.

iPhone How to modify the data contained on the Persistent store using Core Data

I'm stuck trying to figure out how to modify the data contained on the persistent store.
I'm writing an application with several views using a UITabBarController, my core data methods are located mainly on the main application delegate but I will only be using this data from the UItableViewController view.
In order to use the managedObjectContext created in the main application delegate from the UITableViewController I use the following on the viewDidLoad: method:
MessageAppDelegate *appDelegate = (MessageAppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
The application then displays some messages in the table and when a user selects a UITableViewCell (didSelectRowAtIndexPath) I get the ID of the message object and call the following method:
[self readMessage:pk];
-(void)readMessage:(NSInteger)pk {
// First I select the data
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// had to setReturnsObjectsAsFaults to NO so I could access the message data
[request setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Message" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"pk == %d", pk];
[request setPredicate:predicate];
NSError *error;
NSArray *items = [self.managedObjectContext executeFetchRequest:request error:&error];
[request release];
// Then I update the object
for (Message *thisMessage in items) {
//I display the message to the console before updating to check the value
DLog(#"before reading message %#", thisMessage);
// we set the message flat to YES
[thisMessage setRead:YES];
// we set some sample text here (just for testing)
[thisMessage setMessageText:#"New message text"];
// I then display the message to the console checking that the flag and text has been updated
DLog(#"read message %#", thisMessage);
}
// Finally I save the updated message calling the function posted below
[self saveMOC];
}
- (void)saveMOC {
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"there was an error saving the message!");
}
}
After that the data gets updated correctly and if I fetch the data from the managedObjectContext after saving it I get the correct values.
I verified this by adding the following code to at the end of readMessage method:
request = [[NSFetchRequest alloc] init];
//required to avoid presenting objects as faults!!
[request setReturnsObjectsAsFaults:NO];
entity = [NSEntityDescription entityForName:#"Message" inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
//Set the sort descriptor
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"pk" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
//Execute the request
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error later
DLog(#"ERROR: Unable to fetch the results");
}
[self setMessagesArray:mutableFetchResults];
NSLog(#"Data now is: %#", mutableFetchResults);
[mutableFetchResults release];
[request release];
The problem is that if I exit from the application and launch it again all my messages lose the read property (or any other changes I make) and the tableview loads the data as it was first saved onto the persistent store.
Try this and see if the object changes are actually being saved
- (void)saveMOC {
NSError *error;
if (![managedObjectContext save:&error]) {
NSLog(#"there was an error saving the message!");
} else {
NSLog(#"The message was saved!");
}
}
So for each call of saveMOC which is successful, you should see a console message. If it is being called and you're seeing the messages, then you must not be altering the 'read message' property. You could check this by inspecting the value of the 'read message' property before and after setting it either using a breakpoint or by using NSLog messages to print its value
Is -readMessage: method defined in your app delegate or in your view controller? My guess is that you're changing properties of an object in different managed object context than one where you try to save changes (MOC in your app delegate), which actually doesn't have an idea that something has changed. On the other hand, MOC which keeps your changes is never saved (changes are kept only in memory) and that for your changes are lost after you restart your app.
Can this be the situation?