I have UITableViewController with static editable cells. In my UITableViewController I make CoreData object, like this:
- (User*)user {
if (_user == nil) {
_user = [NSEntityDescription insertNewObjectForEntityForName:TABLE_USER inManagedObjectContext:self.managedObjectContext];
NSError* error;
[self.managedObjectContext save:&error];
if (error != nil) {
abort();
}
}
return _user;
}
I want to get the object in NSFetchedResultsController that would display its fields in a table, and then save the object.
I'm trying to do:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:TABLE_USER_PROFILE inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
NSPredicate* pred = [NSPredicate predicateWithFormat:#"%# = %#", TABLE_USER, self.user];
[fetchRequest setPredicate:pred];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:TABLE_USER];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
abort();
}
return _fetchedResultsController;
}
But it didn't work. I get empty fetchedResultsController.
If self.user is a NSManagedObject and you query against a User entity you could try to edit your predicate like the following:
NSPredicate* pred = [NSPredicate predicateWithFormat:#"self == %#", self.user];
Hope that helps.
P.S. If my answer doesn't work try to provide some other details (see my comment)
Related
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.
When inserting in the "FilterProperty" entity, I use the following code in a category "FilterProperty+Manage.h"
+(FilterProperty *)insertFilterPropertyWithName:(NSString *)filterPropertyName forManagedObjectContext:(NSManagedObjectContext *)managedObjectContext forFriendlyName:(NSString *)friendlyName
forDefaultValue:(NSNumber *)defaultValue forMinValue:(NSNumber *)minValue forMaxValue:(NSNumber *)maxValue forType:(NSNumber *)type forBelongsToFilter:(Filter *)filter
{
FilterProperty *fp = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FilterProperty" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name = %# AND belongsToFilter.name = %#", filterPropertyName, filter.name];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil)
{
return nil;
}
if (fetchedObjects != nil && fetchedObjects.count > 0)
{
fp = fetchedObjects.firstObject;
}
else if(fetchedObjects != nil && fetchedObjects.count == 0)
{
fp = [NSEntityDescription insertNewObjectForEntityForName:#"FilterProperty" inManagedObjectContext:managedObjectContext];
fp.name = filterPropertyName;
fp.friendlyName = friendlyName;
fp.defaultValue = defaultValue;
fp.minValue = minValue;
fp.maxValue = maxValue;
fp.belongsToFilter = filter;
fp.type = type;
}
return fp;
}
No while searching, I do the following:
+(NSArray *)getAllFilterPropertiesForFilter:(Filter *)filter forManagedObjectContext (NSManagedObjectContext *)managedObjectContext
{
NSArray *set = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FilterProperty" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"belongsToFilter.name = %#", filter.name];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
return nil;
}
if(fetchedObjects != nil)
{
set = fetchedObjects;
}
return set;
}
The above function when called for any given filter is returning an array with zero objects. I am not able to get why. Any help would be appreciated! Thanks.
You try out this code for searching:-
+(NSArray *)getAllFilterPropertiesForFilter:(Filter *)filter forManagedObjectContext (NSManagedObjectContext *)managedObjectContext
{
NSArray *set = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FilterProperty" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"belongsToFilter.name == %#", filter.name];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
return nil;
}
if(fetchedObjects != nil)
{
set = fetchedObjects;
}
return set;
}
I think in your insertFilterPropertyWithName method you are not saving the new data. If I am correct, try this code after your if-else block:
NSError *error;
if (![managedObjectContext save:&error])
{
//could not save
}
Hope this could resolve your problem.
In that below if condition you need to save in managedobjectcontext.
else if(fetchedObjects != nil && fetchedObjects.count == 0)
{
fp = [NSEntityDescription insertNewObjectForEntityForName:#"FilterProperty" inManagedObjectContext:managedObjectContext];
fp.name = filterPropertyName;
fp.friendlyName = friendlyName;
fp.defaultValue = defaultValue;
fp.minValue = minValue;
fp.maxValue = maxValue;
fp.belongsToFilter = filter;
fp.type = type;
NSError *err = nil;
[managedObjectContext save:&err];
if (errorInWrite != nil) {
//Problem while saving
}
}
Use like instead of "=" Hope it will be useful.
+(NSArray *)getAllFilterPropertiesForFilter:(Filter *)filter forManagedObjectContext (NSManagedObjectContext *)managedObjectContext
{
NSArray *set = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FilterProperty" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"belongsToFilter.name like %#", filter.name];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
return nil;
}
if(fetchedObjects != nil)
{
set = fetchedObjects;
}
return set;
}
I have list of messages in my Core Data store(100 messages).
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"created" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchLimit:20];
It shows only first 20 messages in the order I need.
What I need to do is: Show last 20 messages, sorted by date of creation, but they should appeared form the bottom of the TableView.
Storage:
1 : Test 1
2 : Test 2
3 : Test 3
It shows now as :
1 : Test 1
2 : Test 2
**Supposed to show in table View:**
3 : Test 3
2 : Test 2
1 : Test 1
fetchedResultsController:
- (NSFetchedResultsController *)fetchedResultsController{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Messages" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:20];
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"created" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(owner = %#) and (stream == %#) and (topic == %#)", ownerGuid,openStream,currentTopic];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
self.fetchedResultsController.delegate = self;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[self.fetchedResultsController performFetch:NULL];
return _fetchedResultsController;
}
All you should need to do is change the sortDescriptor to use ascending:NO so that it will get the last 20 messages.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"created" ascending:NO];
Now, you've got the right messages, they're just backwards in your array. You can easily reverse an array using the following code.
NSArray *messages = [[[fetchedMessages] reverseObjectEnumerator] allObjects];
EDIT:
Since you using a NSFetchedResultsController this solution would require a lot of additional code to work.
The easiest solution I can think of would be implement a custom method for getting your objects out of a fetched results controller.
- (id)objectAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger sectionCount = [self.tableView numberOfRowsInSection:indexPath.section];
NSInteger newRowIndex = sectionCount - indexPath.row - 1;
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:indexPath.section];
return [self.fetchedResultsController objectAtIndexPath:newIndexPath];
}
Then just replace
[self.fetchedResultsController objectAtIndexPath:indexPath];
with
[self objectAtIndexPath:indexPath];
#Clever answer is working for me. I called only NSIndexPath (return newIndexPath). I did not override method of the objectAtIndexPath. I was replaced below method.
- (NSIndexPath *)objectNewIndexPath:(NSIndexPath *)indexPath {
NSInteger sectionCount = [self.tableView numberOfRowsInSection:indexPath.section];
NSInteger newRowIndex = sectionCount - indexPath.row - 1;
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:indexPath.section];
return newIndexPath;
}
Then just replace
[self.fetchedResultsController objectAtIndexPath:[self objectNewIndexPath:indexPath];
Is this is correct us of NSFetchedResultsController's getter method with the most modern Object-C and LLVM compiler that doesn't require synthesizing properties?
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController) {
return _fetchedResultsController;
}
if (!self.managedObjectContext) {
self.managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate: self.predicate];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController= [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return aFetchedResultsController;
}
In other parts of my code, I have this instead:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Set" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat: #"sets == %#", self.exercise]];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return self.fetchedResultsController;
}
In your first example, you never assign anything to the property or ivar, so the lazy loading will occur every time the property is accessed. This isn't right.
In your second example you assign to the property, (thereby triggering any side effects of the property setter) and also return the value of the property (which wouldn't take many errors to leave you with an infinite loop). It may work now but it would be easy to break with a careless update.
It would be cleaner to assign to the internal ivar and return that.
So, at the end of your first example:
_fetchedResultsController = aFetchedResultsController;
return _fetchedResultsController;
I am using simple core data fetch request code.
- (NSMutableArray *) read_cd
{
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"List_CD" inManagedObjectContext:cd_Context];
[request setEntity:entity];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"last_date" ascending:NO];
NSArray *sortDescriptor = [[NSArray alloc] initWithObjects:sd, nil];
[request setSortDescriptors:sortDescriptor];
NSMutableArray *mutableFetchResults = [[cd_Context executeFetchRequest:request error:&error] mutableCopy];
if(mutableFetchResults == nil){
//Handle the error
}
return mutableFetchResults;
}
and I'm using return value like this
MSMutableArray *now_cd = self.read_cd;
cell output for {
cell.label1.text = [[now_cd objectAtIndex:i] cd_title];
cell.label2.text = [[now_cd objectAtIndex:i] cd_auth];
}
I want to set default value when executeFetchRequest is null.
so I can continue to use an existing output module.
I don't know how to make a method like that
[[object objectAtIndex:i] instance]