Adding values to a particular index in core data - ios

// deleted all datas for entity before adding
for (int i=0; i<[tempArray count]; i+=3)
{
userData=(UserData *)[NSEntityDescription insertNewObjectForEntityForName:#"UserData" inManagedObjectContext:managedObjectContext];
[userData setUserIcon:tempArray[i]];
[userData setUserID:tempArray[i+1]];
[userData setUserName:tempArray[i+2]];
NSLog(#"loop values%#",[userData userName]);
}
The logged values give names in correct order. But the values stored in core data is not in proper order. E.g. value inside loop shows (apple,ball,cat,dog) but core data stored value shows (apple,cat,ball,dog). I checked values in core data by :
NSFetchRequest *fetchRequest1 = [NSFetchRequest fetchRequestWithEntityName:#"UserData"];
fetchRequest1.resultType = NSDictionaryResultType;
[fetchRequest1 setPropertiesToFetch:[NSArray arrayWithObjects:#"userName", nil]];
fetchRequest1.returnsDistinctResults = YES;
NSArray *dictionaries1 = [self.managedObjectContext executeFetchRequest:fetchRequest1 error:nil];
NSLog (#"names after: %#",dictionaries1);
Any idea why order is mismatching ???
Also alternatively, can I add the values into core data for a particular index so that I can order the table myself?
Or can i use sorting, but i should sort the entire row as the attribute in a row are dependent to each other.
Let me know if you need more information.

I agree with previous answer - your design based on insert order has no practical sense because such order can not be guaranteed and hard to maintain.
The best practice is to apply sort predicates.
For instance you may go to this SO question

Related

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

Updating Core Data Properties WITHOUT using Table Views

I have a POS type app that uses Core Data to store daily sales transactions using table views. I am attempting to retrieve and update certain Core Date Properties, like daily sales counts, WITHOUT using table views. Table views use row at index path to point to the correct object (row). I am using the Fetched Results controller with a predicate to retrieve the fetched object (row) Question: How do I obtain the index of the fetched row so that I can retrieve and then update the correct property values? All books and examples use table views to change properties.
Entity Product
Product *product;
______________________________
[self setupFetchedResultsController]; (This returns one object)
product = [NSFetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; (objectAtIndexPath - Errors of course)
I think you shouldn't use NSFetchedResultsController in this case. If you don't want to use it in either a UITableView or a UICollectionView, you're probably better of without it. You're probably better of using a NSFetchRequest instead, it's pretty easy to set up:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"someValue=1"];
fetchRequest.predicate = predicate;
NSError *error = nil;
NSArray *array = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
Now you have a NSArray with all the results, which you could use without having to deal with index paths.
If you're still using a NSFetchedResultController for a table (I'm not sure if you do), those rows will still be updated whenever you make a change.
Update: To update one of the objects returned by the fetch, could be done like this:
Entity *entity = [array firstObject];
[entity setSomeProperty:#"CoreDataIsAwesome"];
NSError *error = nil;
if ([self.managedObjectContext save:&error]) {
NSLog(#"Entity updated!");
} else {
NSLog(#"Something went wrong: %#", error);
}
You can use the method indexPathOfObject: on your fetched results controller to return the index path of the given object to then do your updates.

Core Data - Select distinct

I know there have been several discussions about this but none of them resolved my simple problem.
I have an Entity called Character and inside there are 4 columns:
character_id, episode_id, title, desc
there can be several same character_ids values but with different episode_id.
When I perform fetch\select I do it for whole table and wishes to get it distinctly by character_id. so this is what I do:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
// Add a sort descriptor. Mandatory.
if(sortDescriptors != nil) {
[fetchRequest setSortDescriptors:sortDescriptors];
}
fetchRequest.predicate = predicate;
// Required! Unless you set the resultType to NSDictionaryResultType, distinct can't work.
// All objects in the backing store are implicitly distinct, but two dictionaries can be duplicates.
// Since you only want distinct names, only ask for the 'name' property.
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:#"title"]];
fetchRequest.returnsDistinctResults = YES;
NSArray *fetchResults = [moc executeFetchRequest:fetchRequest error:&error];
The 'fetchResults' array contains 3 out of 10 rows which is the right result!
The problem: None of the object within the array is accessible.
If I try the following:
NSDictionary item1 = [fetchResults objectAtIndex:0];
NSString *title = [item1 objectForKey:#title"];
I get an exception!
What am I doing wrong?? how can I translate back the dictionary into NSManagedObjects??
Thank you!
First, when using Core Data you should not use foreign keys. Rather, it is preferable to use Core Data's relationships and let the framework deal with the ids in an opaque manner. Maybe you are synching with a web service and need to keep track of the ids but your program logic actually should not depend on it.
Second, if you need an object, it is really better to use the NSManagedObjectResultType rather than the NSDictionaryResultType. You can still obtain distinct results. If you are not experiencing performance issues, this is the preferred pattern. The code is also much more readable.
Your data structure would be this, with a many-to-many relationship:
Character <<--->> Episode
All characters of an episode or all episodes with a certain character is simple. These will be "distinct" results dictated by the logic of the data model:
NSArray *allCharactersInEpisode = episode.characters;
NSArray *allEpisodesWithCharacter = character.episodes;
To select all characters of all episodes you just select all characters. Much simpler than a "distinct" query.

iOS Core Data unique object fetch

I'm fairly new to iOS/Cocoa and I have a very fundamental question about core data. I was looking over the internet to find a appropriate answer/solution but I wasn't able to.
So the question is how do you handle uniqueness in core data? I know that core data is not a database, its something like an object graph. Lets assume we have an entity called 'User' with the attributes 'id' and 'name' and a few relations to the other entities. Now we want to update the name of a specific user (e.g. a web service gave us the id and the new name).
This was the way I have done that before:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"User" inManagedObjectContext:context]];
[request setPredicate:[NSPredicate predicateWithFormat:#"id == %#", anId]];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
User *user = [results lastObject];
But than I've heard this is bad practice. Is it because fetch requests with predicates are very slow? I can't imagine that this is such a big deal. As far as I know there is no other way to get a unique object rather than go over each object and checking for equality..
Would it be more efficient to fetch all objects of the entity, put them in an array and looping through it manually (instead of the fetch request)?
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"User" inManagedObjectContext:context]];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
User *resultUser;
for(User *user in results){
if([user.id isEqual:anId]) resultUser = user;
}
Please help me finding the right path and thanks.
If you have an index for the property that you're fetching (there's a checkbox in the model editor), it's definitely a lot more efficient to use an equality predicate for fetching. Actually, it's never more efficient to fetch all objects and iterate over them in memory, but an index makes the difference more significant.
You're not fetching a unique object but rather objects containing a notionally unique atrribute value.
All managed objects are unique instances but nothing in Core Data enforces that the attribute values are unique. You could in principle have an arbitrary number of unique managed objects all which had identical attribute values. Only relationships enforce a unique position in the object-graph.
There's no particular reason not to fetch a particular object that contains a particular value if that is what your app requires.
I think what you've read is warnings against trying to cram SQL-like key values into entities and then to try and link managed objects together with those keys using predicates, for example doing something like:
EntityOne{
sqlKey:string
}
EntityTwo{
sqlKey:string
}
… and then trying to relate objects of the two entities with predicates.
(anEntityOneObject.)sqlKey == anEntityTwoObject.sqlKey
… instead of just setting a relationship in the data model.
EntityOne{
two<-->EntityTwo.entityOne
}
EntityTwo{
one<-->EntityOne.two
}
… and just finding the related objects with AnEntityOneObj.two.

iOS Core Data - fetching results

My data model is multi-tiered . Let me give an example to demonstrate the data model.
I have three entities say X , Y and Z .
X could have 1 or more Y's.
Y is associated only with 1 X.
Y could have 1 or more Z's.
Z is associated only with 1 Y .
Now my top tableview controller - Controller A uses a fetchedResults controller to get me all the X's from core data
When the user selects a particular row , he/she selects a particular X from which I have the list of all Y's under this X . A particular Y which is passed to controller B . I also pass the managed objected context from A to B .
In B I want to be able to display all the Z's associated with the Y but in some order . Currently I find that the order is indicative of the Most recently used Z . I would want it in the reverse order .
How do I specify ( in terms of core data) to be able to act upon this specific Y and get me results based on a particular criterion ? All the examples for core data that i ve seen so far start from scratch as shown below
NSManagedObjectContext *context = <#Get the context#>;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"<#Entity name#>"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"<#Sort key#>"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error
}
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
One solution to this is to NOT use NSFetchedRequest but to act upon the passed Y object and sort the NSArray .
NSAssert1(yObject!=nil,#"yObject is null",1);
NSSet *zObjects = yObject.zobjectsinY;
NSArray *zObjectArray = [zObjects allObjects] ;
sortedQZArray = [zObjectArray sortedArrayUsingFunction:intSort context:NULL];
I feel that this is not an elegant solution and there should be a better way to query for these results .
Also another option is to be able to insert these values in the expected order when creating the data ? How can I specify the order for a record that is to be inserted in an entity object ?
Thanks in advance
If your getting the reverse of the sort order you want, just reverse the accending; parameter of the sort.
How can I specify the order for a
record that is to be inserted in an
entity object ?
You can't. Sorting is considered an attribute of the interface (because the same data may appear in with many different orders) so managed objects are kept in unordered sets until you fetch them with a sort descriptor.
If you want to save some kind of order in the data, you have to model the order using an attribute or relationship. Usually, however, you want to keep the order "virtual" in that it only appears as needed in the UI.
To get more complex sorts, create a sort with a comparator block. Be warned these can create unanticipated draws on memory and cycles so use them carefully.
You can also create a compare function for custom NSManagedObject subclasses that will compare any two objects of the class in anyway you wish. That is usually not necessary.

Resources