Put Core Data values in an array - objective-c

I need to put the values retrieved using a Fetch Request from my core data graph into an array, but not entirely sure how to go about this.
I'm using the following to perform the fetch:
NSString *entityName = #"Project"; // Put your entity name here
NSLog(#"Setting up a Fetched Results Controller for the Entity named %#", entityName);
// 2 - Request that Entity
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
// 3 - Filter it if you want
request.predicate = [NSPredicate predicateWithFormat:#"belongsToProject = %#", _selectedProject];
// 4 - Sort it if you want
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"dateTaken"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)]];
// 5 - Fetch it
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
As you can see, i'm filtering the values that come back using an NSPredicate.
How would I get these values into an array and also be able to select individual attributes for the entity once they are in the array, for example the project.description or project.name?
Thanks to Eimantas I've got the objects in an array, however I still need to do two things:
Loop through the array and output the data into some HTML
Individually select attributes from the array, for example, the project description.
I'm using the following for loop to do the first:
for (int i=0; i < [projectListArray count]; i++)
{
NSString *tmp = (NSString *)[projectListArray objectAtIndex:i];
}
However, this is returning the error:
-[Project length]: unrecognized selector sent to instance 0x1b9f20
2012-03-28 10:48:35.160 Project App[3973:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Project length]: unrecognized selector sent to instance 0x1b9f20'
It appears as though i might not be incrementing?

[self.fetchedResultsController fetchedObjects] returns the array of fetched objects.
update
It's better to use fast enumaration, not a for loop:
for (Project *project in projectListArray) {
NSString *projectDescription = [project valueForKey:#"description"];
}
You're getting the exception because you're casting an object to NSString while it's a pointer to (I presume) Project managed object.

Related

Retrieving Entities from Core Data: Printing properties gives me incomplete/unwanted results?

I've recently decided to move all my application data from plists to Core Data. This was pretty straightforward. I had a class with a couple properties I would read all my plist info to using a dictionary. This was mimicked by creating an entity with the same exact attributes. I then loaded the plist information into Arrays, and parsed them all, writing and save new entities to core data.
Sounds good no? Well, I thought to so. However, now that I'm reading it all back in, I've noticed some problems.
For one, when I go ahead and try to print off the properties of my object as follows:
for (CXStellarObject *obj in self.starsArray)
{
NSLog(#"Type: %#\n Language: %#\n Name: %#\n ImageName: %#\n Description: %#\n",obj.type,obj.language.label,obj.name,obj.imageName,obj.description);
}
I get this:
Type: star
Language: (null)
Name: Sirius
ImageName:
Description: <CXStellarObject: 0x7fbb0a58d5d0> (entity: CXStellarObject; id: 0xd000000000140000 <x-coredata://9E3F584C-3214-4A6A-B55A-B63D066A152B/CXStellarObject/p5> ; data: {
descriptor = "The brighest star visible from Earth, Sirius (Also known as Sirius A, or the Dog Star), is a bright white dwarf. It is part of the Canis Major Constellation. It is approximately 8.6 light years from o";
imageName = "";
language = "0xd000000000040002 <x-coredata://9E3F584C-3214-4A6A-B55A-B63D066A152B/CXLocalizationAsset/p1>";
name = Sirius;
type = star;
})
For one, my description isn't even complete. It cuts off at "from o", when it actually goes on longer in the plist. Next, name and type are repeated at the bottom of the printed results. I don't know why they're there.
Finally, what are all these addresses and other junk flanking the resulting data? I don't want that there, how did it get there?
You're probably going to need to see what I've got going on here, so I'll show you the Entity that is involved:
Here's how I wrote all these objects to Core Data:
-(void)writeAllArraysToCoreData
{
NSArray *starsArray = [self starsArray];
NSArray *planetsArray = [self planetsArray];
NSArray *moonsArray = [self moonsArray];
NSArray *allData = #[starsArray,planetsArray,moonsArray];
for (NSArray *array in allData)
{
for (NSDictionary *dict in array)
{
[self archieveCXStellarObjectWithLanguage:[dict valueForKey:#"Language"] Type:[dict valueForKey:#"Type"] Name:[dict valueForKey:#"Name"] ImageName:[dict valueForKey:#"ImageName"] Descriptor:[dict valueForKey:#"Description"]];
}
}
}
And finally, the method called in here that I defined:
-(void)archieveCXStellarObjectWithLanguage:(NSString *)languageCode Type:(NSString *)type Name:(NSString *)name ImageName:(NSString *)imageName Descriptor:(NSString *)descriptor
{
CXStellarObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"CXStellarObject" inManagedObjectContext:self.managedObjectContext];
[object setLanguage:[self.supportedLanguageAssets valueForKey:languageCode]];
[object setType:type];
[object setName:name];
[object setImageName:imageName];
[object setDescriptor:descriptor];
// Add New Object to All Objects Array
[self.allStoredObjects addObject:object];
// Save Changes
[self saveContext];
}
And finally how I extracted them, and where this is going a bit awry (Don't worry, it's not a complicated method)
-(void)loadData
{
if (!self.allStoredObjects)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CXStellarObject" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchResults = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (!fetchResults)
{
[NSException raise:#"Fetch Failed!" format:#"Reason: %#",[error localizedDescription]];
}
[self setAllStoredObjects:[NSMutableArray arrayWithArray:fetchResults]];
// Filter Arrays
NSPredicate *starPredicate = [NSPredicate predicateWithFormat:#"SELF.type == 'star'"];
[self setStarsArray:[fetchResults filteredArrayUsingPredicate:starPredicate]];
for (CXStellarObject *obj in self.starsArray)
{
NSLog(#"Type: %#\n Language: %#\n Name: %#\n ImageName: %#\n Description: %#\n",obj.type,nil,obj.name,obj.imageName,obj.description);
}
}
}
And that's it, thanks for taking the time to get this far. If you need any more information from me, just comment and I'll put it in ASAP.
Assuming your CXStellarObject object is a custom subclass of NSManagedObject, you need to override the description method for that object and write it to return formatted info about your managed object.
Something like this:
-(NSString *) description;
{
NSMutableString *result = [[NSMutableString alloc] init];
[result appendFormat: #" descriptor = %#\n", self.descriptor];
[result appendFormat: #" imageName = %#\n", imageName];
//And so on...
}
I suggest investigating Core Data "faults". Whenever you see references like that in your descriptions that is because the object is not in memory yet, only a stub of the object is. As soon as you touch the object Core Data will pull it into memory automatically and then the full values will be visible.
Further, it is common for long description calls to be clipped. If you want to see the full value, capture it in the debugger and call it via po.

Viewing data retrieved from core data stored in an NSArray

I have an iPad app where data is stored in core data. I have used the following code to retrieve the data from the Observations entity which appears to run without any problems and the count of observationList is correct.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Observations"];
NSError *error = nil;
observationList = [[NSArray alloc]init];
observationList = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
However when I try to access the data with the array - for example:
NSLog(#"%#", [[observationList objectAtIndex:0] objectForKey:#"obsDate"]);
It throws an error
reason: '-[Observations objectForKey:]: unrecognized selector
obsDate exists in core data. Can anyone see what I am doing wrong?
NSManagedObject does not implement objectForKey:, you should be using valueForKey: instead. Your code compiles because the array returns an id so the compiler just has to trust that the method you're calling on it will exist at runtime.
Aside: this line is pointless
observationList = [[NSArray alloc]init];
Because you throw away that array instance on the next line.

Having trouble adding objects to NSMutableArray

Im having truble adding objects to my 2 NSMutableArrays. The data comes from a database and I know that my parsing is correct because I get valid outputs when I use the NSLog. However I can't figur out how to add the 2 different objects to my 2 different NSMutableArrays. Here is my code
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
allDataDictionary = [NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
feed = [allDataDictionary objectForKey:#"feed"];
arrayOfEntry = [feed objectForKey:#"entry"];
for (NSDictionary *dictionary in arrayOfEntry) {
NSDictionary *title = [dictionary objectForKey:#"title"];
NSString *labelTitle = [title objectForKey:#"label"];
[arrayLabel addObject:labelTitle];
NSDictionary *summary = [dictionary objectForKey:#"summary"];
NSString *labelSummary = [summary objectForKey:#"label"];
[arraySummary addObject:labelSummary]; //This line makes the application crash
}
}
for some reason when I want to add labelSummary to arraySummary I get this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Any help is appreciated.
Your parsing is indeed correct. However, when the parser comes across an empty field it returns nil. The problem is the that NSArrays cannot accept nils, because nil is not an object, it's equivalent to 0. Therefore, you most add an object. This the role of NSNull.
Must test to see if the parser returns nil, and if so add [NSNull null].
NSString* labelSummary = [summary objectForKey:#"label"];
[arraySummary addObject:(labelSummary!=nil)?labelSummary:[NSNull null];
The error message tells you that one of the objects you are trying to add to the array is nil.
You have to replace
[arrayLabel addObject:labelTitle];
with
if (labelTitle != nil) {
[arrayLabel addObject:labelTitle];
}
and
[arraySummary addObject:labelSummary];
with
if (labelSummary != nil) {
[arraySummary addObject:labelSummary];
}
If you really need to include a nil object, then use NSNull.

NSArray count returns correct number but faults with data access

I am attempting to get a count of items in an array to place it in the right detail of a cell. Interestingly enough I am getting the proper count but my when I try to NSLog the array it returns <fault>.
NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:#"muscleGroup == %# && specificExercise == %# && date >= %# && date < %#", theMuscleGroup, theExercise, fromDate, toDate];
NSFetchRequest *fetchRequestTemp = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"WeightLiftingInfo" inManagedObjectContext:_managedObjectContext];
[fetchRequestTemp setPredicate:searchPredicate];
[fetchRequestTemp setEntity:entity];
NSError *error;
NSArray *tempArray = [[NSArray alloc] initWithArray:[_managedObjectContext executeFetchRequest:fetchRequestTemp error:&error]];
NSLog(#"%#", tempArray);
NSLog(#"%d", [tempArray count]);
My Question:
Basically, what am I doing wrong? Am i initing the array improperly or trying to log it improperly?
I have been researching for hours and trying different coding, but cannot figure it out. Any help would be appreciated. If you need more info or I am doing something against guidelines according to SO please let me know before you close this thread or "-1". HAVE MERCY, I am new and leaning:)
When you execute your fetch request, you get back a managed object fault. That is, a proxy-like object that represents the results even though the real data objects haven't been loaded from the data store yet. The fault knows how many objects are in the array, etc., and when you access the objects the fault will transparently load the real objects.
So, situation normal, don't worry about it. Try using the results instead of logging them and I think you'll find that things work fine.

Why is Core Data losing one of my values?

Inside my user object I have the following code to generate a new 'session' or continue the existing session if one exists.
Strangely it will keep other properties but just loses the 'user' property... user is in a one to many relationship with session, 1 user can have many sessions. (or will do, for the following test I am simply checking for any previous session and using it if it exists)
-(void)setupSessionStuff
{
// Create new Core Data request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Create Sort Descriptors for request
NSSortDescriptor *startTimeSort = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO selector:nil];
[request setSortDescriptors:[NSArray arrayWithObjects:startTimeSort, nil]];
[startTimeSort release];
[request setFetchLimit:1]; // Only get the most recent session
// Execute request
NSError *error = nil;
NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:&error];
if (results == nil) {
// Something went horribly wrong...
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1);
}
[request release];
Session *theSession = nil;
if ([results count] == 1) {
NSLog(#"existing session");
// Use existing Session
theSession = [results objectAtIndex:0];
NSLog(#"session.user: %#", [theSession valueForKey:#"user"]); // this is always null!
} else {
NSLog(#"new session");
// Create new Sesson
theSession = (Session *)[NSEntityDescription insertNewObjectForEntityForName:#"Session" inManagedObjectContext:[self managedObjectContext]];
// Add the Session to the User
NSLog(#"before: session.user: %#", theSession.user); // null
theSession.user = self;
NSLog(#"after: session.user: %#", theSession.user); // looks good
}
...
NSLog(#"before.save: session.user: %#", theSession.user); // good
// Save everything
error = nil;
if (![[self managedObjectContext] save:&error]) {
// Something went horribly wrong...
NSLog(#"Unresolved error: %#, %#, %#", error, [error userInfo],[error localizedDescription]);
exit(-1);
}
NSLog(#"after.save: session.user: %#", theSession.user); // still there..
}
Additionally I have opened up the Core Data sqlite file and examined with SQLite Manager. It looks like the relationship has been correctly saved, as I can see the userID stored in the session table.
-
Just added this at the start of my method as another test.
NSSet *set = self.session;
for(Session *sess in set) {
NSLog(#"startTime %#", sess.startTime);
NSLog(#"user %#", sess.user);
}
Strangely enough the user is set in this case!? So set here then not set a few lines later when I do the fetch request... ?
-
In response to feedback below
Have added this code after assigning session.user = self and both return the expected output. So it does look like the problem is with the subsequent fetch.
NSLog(#"self.session: %#", self.session);
NSLog(#"self.session: %#", [self valueForKey:#"session"]);
Also I agree that accessing my session's through self.session will let me work around my issue, but it doesn't solve what is going on here.
In other places I surely won't be able to walk from one entity to the other so need to confidence the fetch is going to pull everything in correctly.
Firstly, I would check that the relationship is set from the other side by logging self.session. If that shows as null then you have no reciprocal relationship set in the model.
I think that your fetch is poorly constructed. You are relying on the sort descriptor to provide the last used session but you only have a fetch limit of 1. You seem to think that the fetch will find all existing Session objects, sort them and then return the first one. However, sorts execute last so the fetch will find a single session object (because of the fetch limit) and will then apply the sort to that single object. This makes it likely that you will be dealing with a more or less random Session object.
You probably don't need a fetch at all because you are only interested in the Session objects in a relationship with the User object which is self. If you already have the object in one side of a relationship, you don't need to fetch, you just need to walk the relationship. All you really need to do is:
if ([self.session count]==0){
//...create new session and set relationship
}else{
//... find latest session
}
I think your problem with this particular chunk of code is in the reporting. When you get a return value, you use the self.user notation but where you get a null return you use valueForKey:.
While in theory both return the same value, they don't have to because they don't use the same mechanism to return the value. The self-dot notation calls an accessor method while valueForKey: may or may not depending on the specifics of a class' implementation. I would test if the self.session returns a value where valueForKey: does not.
Well I found the problem and solved my issue...
After examining the memory address of my session entity I noticing that it was changing between runs. Investigating further I discovered that where I had been testing some code earlier in another class, creating a new session entity, but not saving it, well, it was being saved after all - when I issued the save in my code above!