Is it possible to remove an object from a fetchedResultsController fetchedObjects?
For example I have the following code:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"pack.packName IN %#", allowedPackNames];
for (int i = 0; i < [tempFetchResults.fetchedObjects count]; i++){
Author *author = [tempFetchResults.fetchedObjects objectAtIndex:i];
NSSet *filteredQuotes = [author.quotes filteredSetUsingPredicate:predicate];
if ([filteredQuotes count] > 0){
author.quotes = filteredQuotes;
} else {
//remove this author from the fetchedObjects array
}
}
How can I do this?
To remove the object from the data store:
[self.managedObjectContext deleteObject:object];
[self.managedObjectContext save:nil];
[self.fetchedResultsController self.fetchedResultsController.fetchRequest];
[tableView reloadData];
To just remove the object from the fetched results controller array, you need to change the predicate. Make sure you disable cache (or change the cache name) for this to work.
I think you could define a property 'excluded' or something like that of integer type and incorporate that property into your fetch request predicate. Now when you mark a quote as excluded, fetched results controller will do all the heavy lifting for you without refetching and all.
Update #1
Addressing the bigger issue here I think using subquery in your fetched results controller predicate is much better idea.
Try using:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(quotes, $x, $x.pack.packName IN %#).#count > 0", allowedPackNames];
as a predicate for your fetch request for fetched results controller and forget about manual filtering.
Basically what this subquery should do for you is filter out all authors that have quotes count fitting your packName criteria. I admit I did not test this query. If you say this won't work for you I will go through the trouble and test it myself.
Related
iCloud doesn't support ordered sets for relationships, only sets. Right now, i've made an attribute called 'entryIndex', where it stores it's own index value that it's given. When the user deletes the object at a specific index, I want to identify the object and change the value of the objects with higher indexes. How do I do this?
Assuming you have consecutive indexes and want to keep them "tight" without gaps:
NSArray *filtered = [fetchedObjects filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"entryIndex > %#",
deletedObject.entryIndex]];
for (NSManagedObject *obj in filtered) {
obj.entryIndex = [NSNumber numberWithInt:[obj.entryIndex intValue]-1];
}
[self.managedObjectContext save:nil];
I create a custom object that has some properties like ID and Title,description etc...
And I add it to an array. (That array may contains more than 500 values).
And I use the following code to retrieve custom objects,
-(CustomObjects *)getObjectWithId:(int)id {
CustomObjects *objCustomObjects = nil;
for (CustomObjects *customObjects in arrayCustomObjects) {
if (customObjects.id == id) {
objCustomObjects = customObjects;
break;
}
}
return objCustomObjects;
}
But It has some performance problem, because I use the function to call on UIScrollview pinch.
How can I improve performance in fetching the objects?
thanks in advance,
A dictionary is better for this. The only catch is that you can’t have a NSDictionary with primitive int keys, so that you have to wrap the id in an NSNumber.
- (void) addCustomObject: (CustomObject*) obj {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
[dictionary setObject:obj forKey:wrappedID];
}
- (CustomObject*) findObjectByID: (int) idNumber {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
return [dictionary objectForKey:wrappedID];
}
A dictionary (also called hash table) does not have to go through all the values to find the right one, it has all the values arranged cleverly according to the keys so that it can jump to the right one or close to it. What you are doing with the array is called linear search and it’s not very efficient.
Better you can use NSDictionary with id as the key. You can easily fetch the object from the dictionary.
Is it Ok for your requirement?
You could use an NSPredicate that checks whether id equals the one you're looking for, and simply filter the custom objects using this predicate by calling filteredArrayUsingPredicate:.
To improve performance, I would try to postpone whatever you're trying to calculate by not directly calling the function that does the heavy work in your scroll view, but rather call [self performSelector:... withObject:nil afterDelay:0]; which postpones the calculation to the next runloop cycle. If you check if there's already a calculation scheduled before you call performSelector you should actually be able to reduce the frequency of the calculation while maintaining a crisp interface.
You must ditch the array in favor for a dictionary if you want to have fast lookups.
If you want to access objects both by key and index then you need to the objects around in two collections, and make sure they are in sync.
I have already done a helper class for this named CWOrderedDictionary. It's a subclass of NSMutableDictionary that allows for access to objects by both keys (as any dictionary do), and by index using methods identical to NSMutableArray.
My class is available to use for inspiration or as is from here: https://github.com/jayway/CWFoundation/
Use NSPredicate:-
You will receive the filtered array with the object that has the id you passed;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", id];
NSArray *filtered = [arrayCustomObjects filteredArrayUsingPredicate:predicate];
Instead of intjust use [NSNumber numberWithInt:] , i did some changes in your given code.
-(CustomObjects *)getObjectWithId:(NSNumber* )id {//changed int to NSNumber
CustomObjects *objCustomObjects = nil;
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF.id==%#",id];
NSArray *result = [array filteredArrayUsingPredicate:bPredicate];
//return filtered array contains the object of your given value
if([result count]>0)
objCustomObjects = [result objectAtIndex:0];
}
return objCustomObjects;
}
I have the following Core Data setup:
Project has-many Answer
Field has-many Answer
Answer has-one Field
Answer has-one Project
I need to find the Answer for each Field that is also owned by Project. I'm currently using a predicate for this and executing a fetch request:
NSEntityDescription *answerEntity = [NSEntityDescription entityForName:#"Answer" inManagedObjectContext:self.managedObjectContext];
NSPredicate *answerPredicate = [NSPredicate predicateWithFormat:#"ANY project == %# && field == %#", self.project, self.field];
NSFetchRequest *answerRequest = [[NSFetchRequest alloc] init];
[answerRequest setEntity:answerEntity];
[answerRequest setPredicate:answerPredicate];
NSError *error = nil;
NSArray *predicates = [self.managedObjectContext executeFetchRequest:answerRequest error:&error];
I'm still new to Core Data but I believe the fetchRequest is querying the database each time I call it, is there a more efficient way of finding these Answer objects?
If I understand correctly, you already have a Field object and an Project object and you want to find the Answer objects they have in common.
If so, the solution is a simple intersect set operation:
NSSet *answersInCommon=[[aFieldObj mutableSetValueForKey:#"answers"] intersectSet:[aProjectObj mutableSetValueForKey:#"answers"]];
... which will return only those Answer objects that appear in both relationships.
Update:
#pdenya in comment provides an enhancement :
Just want to clarify a minor error and a small point that makes this less than ideal. intersectSet returns (void) so the syntax for this would be:
NSMutableSet *answers=[field mutableSetValueForKey:#"answers"];
[answers intersectSet:[project mutableSetValueForKey:#"answers"]];
This solution also modifies the aFieldObj.answers array meaning you can't use this while iterating. setWithSet clears this right up. Example:
NSMutableSet *answers = [NSMutableSet setWithSet:[project mutableSetValueForKey:#"answers"]];
[answers intersectSet:[field mutableSetValueForKey:#"answers"]];
#pdenya's is the correct form.
Best alternate method I've found so far:
NSMutableSet *answers = [self.project mutableSetValueForKey:#"answers"];
NSPredicate *answerPredicate = [NSPredicate predicateWithFormat:#"field == %#", field];
[answers filterUsingPredicate:answerPredicate];
NSManagedObject *answer = nil;
if([answers count] > 0) {
answer = [[answers allObjects] objectAtIndex:0];
}
I am currently having an issue pulling all data from db whereby i.e 1 parameter is TRUE.
I am using NSPredicate and below is a sample code
NSManagedObjectContext *context = managedObjectContext_;
if (!context) {
// Handle the error.
NSLog(#"ERROR CONTEXT IS NIL");
}
NSEntityDescription *entity = [NSEntityDescription entityForName:#"tblcontent" inManagedObjectContext:managedObjectContext_];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"bookmarked == YES"];
[request setPredicate:predicate];
I tried setting predicatewithformat to almost everything but it still does not pull out bookmarks which have a YES value.
I even tried (#"bookmarked == %d",YES) but with not luck. I don't want to have to get the whole array and then filter it manually by doing if(object.bookmarked == YES) .....blabla.
I will really appreciate some help.
Many thanks.
Based on Apple Document Here, we can use the following two methods to compare Boolean:
NSPredicate *newPredicate = [NSPredicate predicateWithFormat:#"anAttribute == %#",[NSNumber numberWithBool:aBool]];
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == YES"];
However, the above predicate cannot get out the ones with empty anAttribute. To deal with an empty attribute, you need the following method according to Apple document here:
predicate = [NSPredicate predicateWithFormat:#"firstName = nil"]; // it's in the document
or
predicate = [NSPredicate predicateWithFormat:#"firstName == nil"]; // == and = are interchangeable here
Sneaking in with the Swift 3/4 answer:
let predicate = NSPredicate(format: "boolAttribute == %#", NSNumber(value: true))
We have to use NSNumber apparently because a literal bool is not acceptable per Apple.
Stolen from here ;)
For some reason, Flow's solution would not work for me:
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == YES"];
However, this did:
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == 1"];
I'm late to the party and as discussed using 0 and 1's is the way to go however there is a better way to show this by using NSNumber BOOL literals like #YES or #NO. It converts it to a 1 or a 0 but is more visually friendly.
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == %#", #NO];
Core Data entities does not have any default values when you create attributes for Entity, so to make your predicate work you should either set default values for boolean attributes or use predicate in this way.
If you provide default value (NO or YES) for any boolean property of Entity then use predicate like this
[NSPredicate predicateWithFormat:#"boolAttribute == %#", #NO];
[NSPredicate predicateWithFormat:#"boolAttribute == NO", #NO];
[NSPredicate predicateWithFormat:#"boolAttribute == %0"];
If you do not have default values or some of Entities was already created without default values then to filter by false value use this sentence:
[NSPredicate predicateWithFormat:#"boolAttribute == %# || boolAttribute == nil", #NO];
Swift:
fetchRequest.predicate = NSPredicate(format: "%K == NO",#keyPath(MyEntity.isOccupied))
or
fetchRequest.predicate = NSPredicate(format: "%K == NO",\MyEntity.isOccupied)
You haven't mentioned what results you're getting. Two things that are missing from your code listing is where you set your request's entity and where you actually ask the context to execute the fetch request. I'd start there.
For me, it happened coz of assign value in Object is wrong when saving.
You have to save like this
YourNSMNanagedObject.visibleBoolean = [[NSNumber alloc] initWithBool:false]
Then all predicate will work when fetching.
E.g.
// Filter only unvisible item
NSPredicate *favouriteFilter = [NSPredicate predicateWithFormat:#"visibleBoolean==NO"];
I keep getting bit by this one and it's not always clear that booleans in core data get saved as NSNumbers.
For the most part I make sure when creating entities I set #NO for any Boolean properties but there's always the case where I spend a whole bunch of time trying to figure out why a test doesn't pass when I've forgot to set #NO on entity creation.
This might not be the best choice for clear code but I've now started to always use != predicates for booleans in core data like (isCompleted != YES) instead of (isCompleted == NO). The fact nil == false pattern is true everywhere else but not in core data predicates and properties can be very confusing.
I need to reload a Person NSManagedObject before I pass it onto the next View.
This is because the fetchedResultsController I'm using is only returning a subset of attributes and I need the full set in the next view.
Thus far I'm trying something like:
- (void)tableView:(UITableView *)tableViewPassed didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Person *partialPerson = (Person *)[self.fetchedResultsController objectAtIndexPath:indexPath];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:[partialPerson.managedObjectContext]];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entity];
...
Now I can't seem to get the predicate to do this working correctly so far I've tried:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF == %#", partialPerson];
and
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF == %#", partialPerson.objectID];
But neither of these seem to work. What am I doing wrong here? Is this a good approach?
Thanks in advance for any suggestions,
Matt
You just need to access the attributes that are not faulted and they will get faulted automatically. You do not need to refetch the object at all.
Update
Storing images or any binary data in Core Data has some basic rules to follow:
< 100kb store it in the same entity
< 1 mb store it in a separate entity on the other end of a relationship
> 1 mb store it on disk and reference it in the same entity
Sounds like you are storing too much binary data in the primary table. To correct this follow the rule above and it will solve your problem.
But that does not negate my original answer in that you can instruct your fetch to pull in attribute1, attribute3 and attribute 5 and when you need to access attribute3, you just access it and Core Data will "do the right thing" and load it when you try and access it. There is never a reason to "reload" the object in this situation.
Why you need to refetch partialPerson?
Simply pass it to the next view and you are done!!! You can do all you want with partialPerson variable on the next view. I don't see any reason why you need to refetch it.
myViewController.myPartialPerson = partialPerson;