call method from NSManagedObject from uiviewController (MVC pattern) - objective-c

My question is simple. How to communicate between my model and my controller ?
Code into controller
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Album" inManagedObjectContext:manageContext];
album = [[Album alloc] initWithEntity:entity insertIntoManagedObjectContext:manageContext];
NSArray *arrayListAlbums = [album returnAllAlbums];
Method into NSManagedObject
(NSArray *) returnAllAlbums
{
NSEntityDescription *entitydescription = [NSEntityDescription entityForName:#"Album" inManagedObjectContext:managedObjContextAlbum];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entitydescription];
NSError *error;
NSArray *arrayListAlbums = [managedObjContextAlbum executeFetchRequest:request error:&error];
return arrayListAlbums;
}

From the code you provided, you have added a convenience method in your NSManagedObject subclass and then you create an instance of the NSManagedObject to call that convenience method. This is not a good design.
You are creating an empty data object to call a fetch method. Perhaps you should have a class method "somewhere" that looks like this:
+ (NSArray *)returnAllAlbums:(NSManagedObjectContext*)moc
{
NSEntityDescription *entitydescription = [NSEntityDescription entityForName:#"Album" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entitydescription];
NSError *error;
NSArray *arrayListAlbums = [managedObjContextAlbum executeFetchRequest:request error:&error];
if (arrayListAlbums == nil) {
NSLog(#"Failed to fetch: %#\n%#", [error localizedDescription], [error userInfo]);
}
return arrayListAlbums;
}
Which you can then access without creating an empty data object:
NSArray *arrayListAlbums = [MyClassWithFetchMethods returnAllAlbums:myLocalContextReference];

Related

I insert my CoreData in background Context and when I fetch, it doesn't show the data

I am trying to do my Core-data operation on background context.
I insert all my data on the background context but when I fetch on main thread, it doesnt show any data which I already inserted.
I dont know, where am I doing the mistake...:(
Any help appreciate.
This Is what I have tried for insert and the second one is for fetch the data.
- (void)insertNameAndSurnameInDataBase:(NSString *)name surname:(NSString *)surname numbers:(NSArray *)numbers labels:(NSArray *)labels {
AppDelegate *appDel=(AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *saveObjectContext = [appDel saveManagedObjectContext];
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.parentContext = saveObjectContext;
[bgContext performBlockAndWait:^{
NSError *error;
NameAndSurname *nameAndSurname = [NSEntityDescription insertNewObjectForEntityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
NSString *nSurname = [NSString stringWithFormat:#"%# %#",name,surname];
if (nSurname.length == 0) {
nameAndSurname.nameSurname = [numbers objectAtIndex:0];
} else {
nameAndSurname.nameSurname = nSurname;
}
if ([bgContext save:&error]) {
} else {
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
// predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nameSurname =[c] %#", nSurname];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *fetchedObjects = [bgContext executeFetchRequest:fetchRequest error:&error];
if([fetchedObjects count] > 0){
[numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
id obj2 = [labels objectAtIndex:idx];
obj = [obj stringByReplacingOccurrencesOfString:#" " withString:#""];
ContactNumber *phoneNumber = [NSEntityDescription insertNewObjectForEntityForName:#"ContactNumber" inManagedObjectContext:bgContext];
phoneNumber.number = obj;
phoneNumber.label = obj2;
phoneNumber.nameAndSurname = fetchedObjects[0];
NSError *error;
if ([bgContext save:&error]) {
} else {
}
}];
}
}];
}
- (NSArray *)fetchNameAndSurname {
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
_managedObjectContext = [appDel managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setSortDescriptors:#[[[NSSortDescriptor alloc] initWithKey:#"nameSurname" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)]]];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
return [fetchedObjects valueForKey:#"nameSurname"];
}
The mistake you are making is to not test the many little pieces of this code which could be not working as expected. To troubleshoot something like this, you must start at the beginning and test one piece at a time. The first issue I see is confusion between managed object contexts…
It appears that the method saveManagedObjectContext is a getter for your main managed object context. In English, save is a verb, so that this method name implies that it is saving some managed object context. If I am correct, you should change the name saveManagedObjectContext to maybe mainMangaedObjectContext.
The statement _managedObjectContext = [appDel managedObjectContext] is quite nonstandard, assigning to an instance variable from what appears to be its getter. If, as usual, _managedObjectContext is the instance variable backing the property managedObjectContext, this statement has no effect.
In your first method you use [appDel saveManagedObjectContext], and in your second method you use [appDel managedObjectContext]. It looks like these should be the same context in order for your fetch to work. Are they?
UPDATE:
As you stated in your comment, that fixes the saving, but now you have a performance problem – saving to the persistent store on disk blocks the user interface, which is what your original code was trying to fix.
This is a solved problem. The solution is that your main-thread context, which interfaces the user, should be a child of your background-thread context which interfaces to the persistent store. It is explained nicely with code in this 2015 blog post by Marcus Zarra. For further reading, Chad Wilken has published a slight variation. Both are written in Objective-C for you :)

How can i replace Object in NSManagedObjectContext

How i can replace object with managedObject in core data object.Please help me how i can out from this problem.
XMPPMessageArchivingCoreDataStorage *storage = [XMPPMessageArchivingCoreDataStorage sharedInstance];
NSManagedObjectContext *moc = [storage mainThreadManagedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"XMPPMessageArchiving_Contact_CoreDataObject"
inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSSortDescriptor *sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey:#"mostRecentMessageTimestamp" ascending:NO];
[request setEntity:entityDescription];
[request setSortDescriptors:#[sortDescriptor1]];
NSError *error;
NSArray *data = [moc executeFetchRequest:request error:&error];
for (XMPPMessageArchiving_Contact_CoreDataObject *managedObject in data) {
if ([managedObject.bareJid isEqual:_user.jabberId]) {
// [moc deleteObject:managedObject];**//Here i want to replace object to managed object never delete**
}
}

Decrypting core data

Warning: I don't know obj C but was handed this project for this one task
I am successfully encrypting the fields which lay in core data (sqlite) but I cannot decrypt them using a similar method. Below is my code which is causing the app to break during the fetch of core data:
NSManagedObjectContext *context = [self getContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
[request setReturnsObjectsAsFaults:NO];
if (predicate != nil) {
[request setPredicate:predicate];
}
NSError *error;
//the code that breaks
NSMutableDictionary *theFetch = [[context executeFetchRequest:request error:&error] mutableCopy];
return [[self decryptDictionaryData:theFetch withUserId:entityDesc.propertiesByName[#"theUnlockKey"]] mutableCopy];
decryptDictionaryData goes through each key in the dictionary and decrypts as follows:
decryptedResult = [RNDecryptor decryptData:[[NSData alloc] initWithBase64EncodedString:value options:0]
withSettings:kRNCryptorAES256Settings
password:key
error:&error];

controllerWillChangeContent not being called when deleting all entries of an entity

Below is the method I have implemented for a tableView "reset" button. I've verified that the entries in the Entity are being deleted, however my controllerWillChangeContent is not being called after the deletion. Is there a way to call this method manually?
CoreDataStack *coreDataStack = [[CoreDataStack alloc]init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"FoodEntry"];
[fetchRequest setIncludesPropertyValues:NO];
NSError *error;
NSArray *fetchedObjects = [coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[coreDataStack.managedObjectContext deleteObject:object];
}
error = nil;
[coreDataStack.managedObjectContext save:&error];
EDIT : FRC CODE
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self entryListFetchRequest];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:coreDataStack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Can't be certain, without knowing the internals of CoreDataStack, but it looks like you are using two different stacks: notice that your code uses:
CoreDataStack *coreDataStack = [[CoreDataStack alloc] init];
whereas the FRC uses:
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
You could change your code to use the same as the FRC (defaultStack is probably a singleton and will return the same stack whenever it is called). But if the FRC is in the same class as the delete code, it will be easier (and clearer) to just use the FRC's managedObjectContext:
NSManagedObjectContext *context = self.fetchedResultsController.managedObjectContext
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"FoodEntry"];
[fetchRequest setIncludesPropertyValues:NO];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[context deleteObject:object];
}
error = nil;
[context save:&error];
Incidentally, it's always worth checking if the executeFetchRequest: and save: return an error.

Does CoreData always respect returnObjectsasFaults?

In the following code, I explicitly set returnObjectsasFaults as false. Then RIGHT after the request I check to see if objects are fault or not. NSAssert fail.
Perhaps it's because the object is an imageBlob. Perhaps I am missing something? I just want to make sure.
This is a minor issue. If I get rid the nsassert, then my programs will run anyway. Still it sucks.
+(NSFetchRequest * )fetchRequestInContext:(NSString*) entityName:(NSPredicate *) predicate:(NSString*) sortKey:(BOOL) sortAscending
{
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[BGMDCRManagedObjectContextThreadHandler managedObjectContext]];
[request setEntity:entity];
if(predicate != nil)
{
[request setPredicate:predicate];
}
if(sortKey != nil)
{
NSMutableArray * sortDescriptorArray =[self getMoreSearchDescriptorsForEntity:entityName];
[request setSortDescriptors:sortDescriptorArray];
}
//request.fetchLimit = 200; //can be overridden somewhere else
request.returnsObjectsAsFaults = false;
if (entityName == BusinessString)
{
request.relationshipKeyPathsForPrefetching = arrayRelationship;
}
//[request setIncludesSubentities:<#(BOOL)#>
return request;
}
+(NSArray *) searchObjectsInContextEntityName:(NSString*) entityName Predicate:(NSPredicate *) predicate SortKEy:(NSString*) sortKey Booelan:(BOOL) sortAscending
{
NSManagedObjectContext * moc =[BGMDCRManagedObjectContextThreadHandler managedObjectContext];
NSFetchRequest *request = [self fetchRequestInContext:entityName:predicate:sortKey:sortAscending];
NSError *error;
if (entityName==BusinessString)
{
error=nil; //Some code for breakpoint
}
NSArray *fetchedObjects = [moc executeFetchRequest:request error:&error];
for (NSManagedObject * mo in fetchedObjects) {
NSAssert(!mo.isFault, #"For some reason mo is fault");
}
return fetchedObjects;
}
I've encountered the same problem while operating on a child NSManagedObjectContext. I create one as follows
NSManagedObjectContext *workerMOC = nil;
workerMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerMOC.parentContext = self.moc; // this is my main NSManagedObjectContext
Now after that if I do:
[workerMOC performBlock:^{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Company"];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *allCompanies = [workerMOC executeFetchRequest:fetchRequest error:nil];
}];
I get faults in allCompanies. Which of course, just to clarify, does not happen if I execute the fetch request on self.moc.
However, I get appropriate pre-fetched results if I use the approach below:
[workerMOC performBlock:^{
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
fetchRequest.entity = [NSEntityDescription entityForName:#"Company" inManagedObjectContext:workerMOC];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *allCompanies = [workerMOC.persistentStoreCoordinator executeRequest:fetchRequest
withContext:workerMOC
error:nil];
}];
So it seems to be the case, that fetching on NSManagedObjectContexts tied directly to the NSPersistentStoreCoordinator works just fine. But in case of child NSManagedObjectContexts, which are not tied directly to the store, but rather to the parent context, do not behave as expected.
I could not find anything related to this matter in the Apple docs, but still, I don't think it's a bug.