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.
Related
I am using Core Data in my application where I am trying to retrieve entities whose relationship objects attribute match my criteria. Unfortunately I am stuck here, because I am passing the id for the relationship object, but I am getting an error saying that the criteria I am passing is not being recognized as an attribute of the entity I am querying.
This is true, because my criteria is actually an attribute for the relationship object, and not an attribute of the entity that I am querying itself. How do I achieve this?
Here is my code:
// Create fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:myEntityName inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Create predicate
NSPredicate *pred = [NSPredicate predicateWithFormat:#"relationshipObjectId == %#", relationshipObjectId];//This is where I am having trouble
[fetchRequest setPredicate:pred];
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
if ([items count]>0) {
return items[0];
} else {
return nil;
}
Can anyone see what it is I am doing wrong?
To find all related objects whose attribute matches a certain value, you would
use a predicate like
[NSPredicate predicateWithFormat:#"rel.attr == %#", value]
where "rel" is the name of the relationship, and "attr" is the name
of the attribute that should match the value.
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.
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.
I have a NSManageObject set up as an Entity in Core Data. After fetching the Entity, I want to be able to pull all of the attributes and put them into an NSMutableArray to populate a UITableView with.
For Example:
Entity:
Project
Attributes: startDate(required); finishDate(optional); projectName(required); etc....
How do I get all of these into an NSMutableArray? Or is there a better way to populate the UITableView?
You can get this by asking the NSEntityDescription for its NSAttributeDescription objects:
NSManagedObject *object = ...;
NSEntityDescription *entity = [object entity];
NSDictionary *attributes = [entity attributesByName];
NSMutableArray *values = [NSMutableArray array];
for (NSString *attributeName in attributes) {
id value = [object valueForKey:attributeName];
if (value != nil) {
[values addObject:value];
}
}
Note: this only contains attributes, not relationships. If you want only relationship values, you can use -relationshipsByName. If you want both attributes and relationships, you can use -propertiesByName.
Deciding whether this is a good idea or not is left as an exercise to the reader.
Can't you just use executeFetchRequest to get the array?
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Project"
inManagedObjectContext:someContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [someContext executeFetchRequest:fetchRequest error:&error];
EDIT
Simply add a method to your entity that returns an array of non-null attributes:
- (NSMutableArray*)nonNullAttributes {
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:0];
//Pretend you have an attribute of startDate
if (startDate && startDate != null) {
[mutableArray addObject:startDate]
}
//Do this for all of your attributes.
//You might want to convert the attributes to strings to allow for easy display in the tableview.
return mutableArray;
}
You can add this in your NSManagedObject subclass. You can then use the count of the array to know how many rows to have.
ORIGINAL ANSWER
Why even bother putting the attributes into an array? Just access them from the Entity directly when you populate the tableview.
I m trying to update some records in Core Data. I m adopting following steps to get it done
Fetch function with predicate retrieves the records from the Core Data
Store the result set in a Object Array
Loops through the array and update each record
Call save context
I m running into two problems
After Initial run i get < fault > in the log
I m not sure whether the save context will actually save the object
My code:
- (void)fetchExpenses {
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Expense" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"publishTimestamp == nil"];
[request setPredicate:predicate];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setExpenseArray: mutableFetchResults];
[mutableFetchResults release];
[request release];
}
- (void) save: {
[self fetchExpenses];
int i = 1;
int max = [expenseArray count];
for(i=1; i<=max; i++) {
// Get the expense selected.
Expense *expense = [expenseArray objectAtIndex: i];
// Do your updates here
[expense setTimestamp:2]
}
}
The fault you are seeing in the log doesn't indicate an error but means that the managed object is not fully loaded into memory but is instead represented by a fault object. This is normal behavior. When you try to access or change an object attribute the full object will be "faulted" or read-in to memory. It's a confusing old-fashion database terminology that dates back to 1960s.
Your code does not save any objects. Changes to managed objects in memory will not be persisted until you call a save on the managed object context.
You also do not want to use a mutable copy like this:
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
… because it waste memory and can lead to duplicate managed objects. There was some code in Apple docs that got this started but its erroneous. Instead, just use:
NSArray *fetchResults=[managedObjectContext executeFetchRequest:request error:&error];
… which will return an autoreleased array of the managed objects matching the fetch.