Search Core Data Users By Name - ios

I am trying to get a list of users whose names were selected from a tableview and stored in the array selected.
What's wrong with my code? Sorry this is my first time with CoreData so I don't really know what's wrong.
NSFetchRequest *request= [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AUser" inManagedObjectContext:_managedObjectContext];
for (id a in selected) {
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"name==%#",a]; //each user has a name attribute
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *array = [_managedObjectContext executeFetchRequest:request error:&error];
//This array is always empty! Why? What am I doing wrong with the request
}
Tell me if you require any more info/further explanation.
Other things to note - there are no warning/error signs - all variables not declared here are declared elsewhere
Thanks a lot!

Since you're not getting any error response, an empty array means that the fetch completed successfully but that it didn't find anything satisfying your predicate. In your code that then implies that selected does not contain values for name that can be found in your data store.
Your code seems to assume that selected is an array of NSStrings which match the name value of some of your stored objects. If that's not the case-- if it contains something other then NSString maybe-- an empty set is to be expected. The fact that a is declared as an id makes me wonder what's really in there.

Related

Fetching entity attributes directly from coredata entity

I have an Entity called Devices in CoreData. Devices have following attributes:
Name, model, manufacturer, owner. All attributes are of string type. I am using the following code to fetch values from Entity devices using predicate
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Devices" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
request.fetchLimit = Device_LIMIT;
NSSortDescriptor *sortDisc = [NSSortDescriptor sortDescriptorWithKey:#"model" ascending:YES];
[request setSortDescriptors:#[sortDisc]];
NSError *error;
NSArray *array = [self.managedObjectContext executeFetchRequest:request error:&error];
if (array) {
arrName = [array valueForKey:#"Name"];
}
return arrName;
I am getting the desired result this way.But, my question is how can I directly fetch an array from Device Entity for Name attribute, without adding any if-else or for in condition.
You need to fetch an array from Core Data to get these values-- that's just how Core Data works. You could change your code to use NSDictionaryResultType, but then you'd just get an array of dictionaries instead of an array of managed objects. What you're doing is correct, an there's no short cut to get name values without getting an array and extracting the names from that array.
Do a for-loop to add values to arrName as:
NSMutableArray *arrName = [NSMutableArray new];
for (NSManagedObject *object in array) {
[arrName addObject:object[#"Name"]];
}
A nil array is only returned if the fetch request failed with an error.
Your code should be structured such that if there is no array, you should deal with the error.
EDIT: This is fairly true for most methods that return something and take an error as an argument in cocoa. Usually if nothing is returned, then the error should be checked.
The return value for the method executeFetchRequest is an objects array, so you must use a for-loop to take the name attribute out.

executeFetchRequest is not working with propertiesToFetch

I have integrated coreData in my application. I am running NSManagedObjectContext in the main thread.
-(NSArray *) getResultForContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"Person"];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
[request setEntity:entity];
NSError *fetchError;
NSArray *allObjects = [self executeFetchRequest:request error:&fetchError];
return allObjects;
}
The above method is working fine but if I add propertiesToFetch to the request, executeFetchRequest return an empty array.
-(NSArray *) getResultForContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"Person"];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
[request setEntity:entity];
[request setResultType:NSDictionaryResultType];
[request setReturnsDistinctResults:YES];
[request setPropertiesToFetch:#[#"firstName",#"lastName"]];
NSError *fetchError;
NSArray *allObjects = [self executeFetchRequest:request error:&fetchError];
return allObjects;
}
This method returns an empty.
What is missing here?
Assuming that (1) data is present (i.e. that there actually are some Person instances to be fetched) and that (2) the result is actually an empty array and not nil:
The only way I know to get a non-empty array in the first case but an empty array in the second case would be if the listed properties are both nil for all instances. In that case, there are no values, so the call to setReturnsDistinctResult effectively filters out every result (because nil is not considered a distinct result).
If either property is non-nil for any existing Person instance, you'll get a non-empty array. But if there are no property values to fetch, there are no distinct results, and the result is empty.
If it happens that the result is actually nil, and not an empty array, look at fetchError for clues.
The problem comes from the NSDictionaryResultType.
As said bbarnhart on this post, you must save the context to persistent store before using the resultType NSDictionaryResultType.
Be careful to the declaration of the context persistentStoreCoordinator too.

Core Data, how can i find and delete managed objects effectively

My app sends a get request to a server with a date (date of last update) to update its content (2 entities in core data, no relationships, all attributes are strings)... most of the time it only receives new content but sometimes it also receives the old content that needs to be updated instead of just inserted. 1 table is pretty straight forward I just get the ids (id from server not form core data) of the items that are going to be updated in an array and I make a fetch of those items and then delete them. After that I insert the updated items as they were new. This is how I delete them:
-(void)deleteOfEntity:(NSString*)entityName theItemsWithIds:(NSArray*)ids{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext: [self managedObjectContext]];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesPropertyValues:NO];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"(id IN %#)", ids]];
NSError *error;
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if(fetchedObjects && [fetchedObjects count]>0){
for (NSManagedObject* toDelete in fetchedObjects) {
[[self managedObjectContext] deleteObject:toDelete];
}
}
}
because the attribute name which identifies each item is the ID as usually. But the other table needs 2 attributes to identify items, like a composite key. How do I build up the array of "ids"? an array with arrays of 2 values indicating the composite key? and the predicate? I just want to know if it is possible to do this efficiently, if not I can always fetch all the items and check 1 by 1 but for that I need a for inside another for and that is to ugly. Any help is appreciated.
When you designed the database you should have created a unique key field, even if it is just a composite of the two values. That would have made this question go away.
However, to solve the problem now you need to do a fetch on one key similar to what you have above and then loop over the second key. However, you do not need to do a loop within a loop. You can use a second NSPredicate against that returned array to get the objects to modify.
Less ugly and quite efficient since you are only going to disk once and the second filter is happening in memory.
Update
#DuncanGroenwald is correct that you still must loop through every object, but there is looping and there is looping.
A developer writing a for loop and then doing a string compare inside of that for loop is significantly less efficient then letting the frameworks perform the same option. How? With a NSPredicate against the array:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"someValue IN %#", idArray];
NSArray *results = [origArray filteredArrayWithPredicate:predicate];
If you test both options, the predicate will run significantly faster.
Well what I did is to create another attribute named "identifier" which is a stringWithFormat:#"%#%#",key1,key2, it doesn't matter the extra string in coredata because it suppose to have just a few managed objects in that entity

Reassigning sorted NSManagedObjects to the NSSet

I'm just getting started with Core Data and am not sure how this works. I basically have a Person entity and an alarm entity. Each person can have many alarms. What I want is to go to a detailViewController of the person object and see their alarms. Because NSSet isn't sorted, I have a method to return the alarms sorted like so:
- (NSArray *)sortedTimes {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Alarm" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *timeDescriptor = [[NSSortDescriptor alloc] initWithKey:#"time" ascending:YES selector:#selector(compare:)];
[request setSortDescriptors:#[timeDescriptor]];
NSError *error = nil;
NSArray *objects = [self.managedObjectContext executeFetchRequest:request error:&error];
// Can I do this???
//self.person.alarms = [NSSet setWithArray:objects];
// for (NSManagedObject *obj in objects) {
// NSDate *date = [obj valueForKey:#"time"];
// NSLog(#"date: %#", [date description]);
// }
return objects;
}
What I'm wondering is, in the line self.person.alarms = [NSSet setWithArray:objects]; is that ok? I guess I'm not sure as to what actually is happening. My executeFetchRequest returns an array of the objects I want. Can I just go ahead and assign it to the person entity's alarm property? I wasn't sure if there was a relationship from Person->Alarm that I should not be mucking with, or if something like this is perfectly legal. Thanks!
First of all, your fetch request returns all alarms, not only the alarms of self.person. You have to add an predicate to the fetch request:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person = %#", self.person];
[request setPredicate:predicate];
(assuming that person is the inverse relationship from the Alarm entity to the Person entity). But you don't really need a fetch request to get the sorted alarms of a person. A more direct way is
NSArray *objects = [[self.person.alarms allObjects]
sortedArrayUsingDescriptors:#[timeDescriptor]];
Now to your question: The statement
self.person.alarms = [NSSet setWithArray:objects];
just re-assigns the same set of alarms to the person. This effectively does not change anything, because it is the same set. In particular, it does not guarantee that self.person.alarms will now be sorted by time.
Remark: It you want to display a table view with the alarms of a person, you can also use a NSFetchedResultsController (FRC) as table view data source. The advantage of using a FRC is that the table view is automatically updated if objects are inserted, removed or updated.
Have a look at the NSFetchedResultsController and NSFetchedResultsControllerDelegate documentation which contains all the required code templates.

Core Data Fetch Request Variable in Xcode 4

How do I add a variable to a fetch request in core data in Xcode 4? Can't find it.
Select Expression from the drop down and enter the expression using a $ before your substitution variable (NAME in the example below). Even if the substitution variable's value is a string, make sure you don't put the variable between quotes, or the substitution will not work.
In your code you refer to the fetch predicate like this (XCode 4.4 and above):
NSManagedObjectModel* model = [[context persistentStoreCoordinator] managedObjectModel];
NSFetchRequest* request = [model fetchRequestFromTemplateWithName:templateName
substitutionVariables:#{#"NAME" : name}];
NSError* error = nil;
NSArray* results = [context executeFetchRequest:request error:&error];
Apple has moved it again in the latest XCode. To use Variables in the Fetch Requests in the Core Data editor, you have to use the "expression" type and manually type it in:
data == $DATA
(in this expression, $DATA is the variable).
If by variable you mean pass a value to a fetch request so you delimit the request? Then all you do is declare an NSPredicate.
Something like this:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"name" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(attribute == %#)", value];
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error;
NSArray *results = [context executeFetchRequest:request error:&error];
[request release];
You may have already found a solution but it took me a while and it may save someone else's time so here it is: the apple documentation explains it.

Resources