Access related objects in Core Data - ios

I've made a relationship between my Songs and Playlists entities. Now I'm trying to access the Songs in the selected Playlist. The selected playlist is saved in the object
optionsSingle.selectedRowNumber
I have this fetch method where I'm accessing all the songs object and save them in a NSMutableArray called playlists. How do I only fetch the songs which are related to the give playlist object (optionsSingle.selectedRowNumber)?
- (void)fetchDevices {
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Playlists"];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Playlists" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
playlists = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
Core Data Model:

I think you have a better design choice. While the corrections suggested by Sebastian are very important, you should redesign your table view. (I infer it is a table view displaying your playlists from your question).
You should use a NSFetchedResultsController rather than creating an array and populating your table with the contents of the array. The FRC will take care of all the fetching and memory management specific for the table view.
You can get a working example of a NSFetchedResultsController easily by opening the master-detail template ("New Project" in Xcode) and checking the "Core Data" option.
Now you can get to the playlist very easily:
Playlist *playlist = [self.fetchedResultsController objectAtIndexPath:indexPath];
You will find that it will be much easier to write clean, concise and readable code.

There are some issues with your code and model. Entity classes should be named singular (Song and Playlist).
The method is called fetchDevices (seems like a copy&paste error and should rather be fetchPlaylists or something?)
You are doing too much to retrieve them. The method can be reduced to:
- (void)fetchPlaylists {
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Playlist"];
playlists = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
You can also try to reduce the relation so that one Playlist has many Song(s). That should be enough for your requirements. Then you can proceed and access a list of songs that will automatically be fetched with your playlist.

Related

Does Core Data persist the order of objects in NSMutableArray?

I basically operate on objects in a NSMutableArray, which is fetched from Core Data. All changes to those objects save in Core Data with the exception of the order of objects in the array. When, for instance, an object in the array is moved to the end of it, that does not seem to save in Core Data. Is it the case that Core Data does not persist order of objects in arrays? If so, what can be done to work around it? Sorting?
This is how I fetch data:
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Item" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
self.items = [[managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
And this is how I save changes:
- (void)saveCoreData{
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error;
if (![context save:&error]) {
NSLog(#"Could not save data: %#", [error localizedDescription]);
}
}
If you're saving the array in a transform able property on an entity then the order will be saved - if you set the array back onto the managed object instance.
If the array is a fetched list of managed object instances then the order of that array means nothing and won't be saved. If you want to save this order then you need to add (and update) some other data in the context. This could be an order attribute on the entity or another entity with an ordered relationship perhaps.
If you're not using ordered relationships, then there is no guarantee of the order.
You can either set your relationships to be ordered. In this case you will have to deal with NSOrderedSet and different accessory methods. This feature is available in iOS5 and later.
Here is a great article of Ash Furrow (great developer, had a privilege to meet him) that covers ordered relationships in Core Data.
On the other hand, you can order your data once you access it. In my case I had an NSArray property that, once accessed, would get all objects in NSSet and order them. The disadvantage of this approach is every time you add new NSManagedObject to a relationship, mentioned NSArray will become outdated and must be recreated.

Core Data: unnecessary queries?

Good evening,
I need some help with Core Data. I have two entities, Images & Album (ImageGroup in code), that have a many-to-many relationship. Each image can have many Albums and an Album can have many images.
In the application, the user can create an Album. Once the album is created, all the new pictures get added to that album. This is working fine.
But now I have a question regarding the fetch request in Core Data. Please have a look at my current "fetch" code:
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ImageGroup" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
// Load all the other albums
for (ImageGroup *group in fetchedObjects) {
NSString *albumName = group.name;
NSSet *groupSet = group.group;
for (Images *image in groupSet) {
NSLog(#"imageTN: %#", image.nameTN);
}
}
This is working fine as well. However, during the "for in" loop for groupSet, a select query is executed for EACH step in the loop. This is fine if you only have a few images, however, if you have 1000+ images stored, this code would execute 1000+ queries.
Is there a way to load all the images that are associated with the Album in one single query?
(SELECT * from Images WHERE album_ID = ? something like that).
This is the first time i'm using Core Data and not sqlite. Any help is truly appreciated.
This is possible, if you don't go through the set, or you prefetch the images in group (since your current fetch request fetch ALL ImageGroup entities, you probably should avoid that).
To prefetch the to-many relationship add to your request:
[fetchRequest setRelationshipsForPrefetching:#[#"group"]]
i would also add:
[fetchRequest setIncludesPropertyValues:YES] and:
[fetchRequest setReturnsObjectsAsFaults:NO]
If you want to issue a single request per ImageGroup you could use:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Images"];
[fetchRequest setIncludesPropertyValues:YES]
[fetchRequest setReturnsObjectsAsFaults:NO]
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"%# IN <albom set>",group.objectID]; //not tested
note: by the end of the external loop you have faulted (allocated in memory) all alboms and all their attached images.
You should fault each ImageGroup you are done with to keep memory peeks at bay.

How to change value of an attribute of all entities in Core Data at once?

I am building application that has 'checklist' functionality. The checklist item is stored as Core Data entity. There is a 'checked' attribute stored as BOOL in the Datamodel as well. The view controller handling this checklist functionality is based on UITableViewController.
Basically, I would like to implement the UIRefreshControl which allow users to reset the 'checked' status of all checklist entities in Core Data. For example, all the items would be reseted and shown as 'unchecked' once user pulls down the UITableView.
However, NSFetchedResultsController only provides access to one entity at a time via [fetchedResultsController objectAtIndexPath:indexPath]. Would there be a way to get the whole collection of entities from Core Data as NSArray or NSDictionary therefore I could enumerate all entities and change their 'checked' attribute?
Agrees with coverback...lets say you want to fetch all the objects from the entity named "Test":
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Test"
inManagedObjectContext:context];
NSError *error;
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
fetchObjects array contains all the objects in "Test" entity
[fetchedResultsController fetchedObjects] will do just fine for your task.
In this case it is also not needed to use NSFetchedResultsController, you can get away with a simple NSFetchRequest.
I needed to update a lot of entities so the solution using NSFetchRequest were too slow. Luckily Apple added NSBatchUpdateRequest in iOS 8. Here's a simple example:
NSBatchUpdateRequest *batchUpdate = [[NSBatchUpdateRequest alloc] initWithEntityName:#"EntityName"];
batchUpdate.propertiesToUpdate = #{ #"attribute": #(0) };
batchUpdate.resultType = NSBatchDeleteResultTypeStatusOnly;
[managedObjectContext executeRequest:batchUpdate error:nil];
And here's a good blog post on the subject: https://www.bignerdranch.com/blog/new-in-core-data-and-ios-8-batch-updating/.

Core data sorting a Thread by latest Message (to many relationship sorting)

My question, I believe, is fairly simple but I can't seem to find a decent and elegant solution to it. Maybe Core Data doesn't have an elegant solution or my Google skills have let me down, in which case I apologize.
Lets say I have a simple messaging application. I have two Core Data entities: Thread and Message. A Thread contains many Messages.
My application should open with a UITableView in which all the Threads are listed. The application will use RestKit and its object mapping mechanisme to load the data. The Messages have a created property.
So I want to sort my list of Threads so that the Thread with the latest Messages appears on top and the thread with the oldest messages in it will apear on bottom.
I believe this is also what happens in Apple's own messaging application, and it uses Core Data as well. This is the main reason I hope that there is a more elegant solution than for example store an extra to one relation to the latest message in the database. Which is in my case unpractical because I use RestKit object mapping and don't have influence on the HTTP API.
I've tried transient values, this doesn't work because you can't sort on them. I have tried a sort descriptor with a NSComparisonResult block, but this also doesn't work because core data says it can't sort on to-many relationships. And I've looked into fetched properties, but I can't figure out what predicate to use.
Thanks in advance.
You can create a readonly lastMessageDate property for your Thread class. Implement your own getter to return the newest date from threads messages. Then sort your threads using this property:
NSArray *sortedThreads = [threads sortedArrayUsingComparator: ^(Thread *thread1, Thread *thread2) {
return [thread2.lastMessageDate compare:thread1.lastMessageDate];
}];
Did you try NSSortDescriptor while fetching the Core Data Object like:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:YOUR_ENTITY inManagedObjectContext:[AppDelegate objectModel]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"createdOn" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error;
NSMutableArray *mutableFetchResults = [[[AppDelegate objectModel] executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
NSLog(#"Fetching data failed: %#", error);
}
return mutableFetchResults;

Can I use NSFetchedResultsController for the display of data not meant for a UITableView?

In the FRC documentation, it says that it is intended to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.
I am trying to setup my Core Data stack to handle the storage and retrieval of some VERY basic data. Can I not use FRC? I need to display a value set in the db to a UILabel, what is the best Core Data method to use for that?
I have a core data book I am trying to get through, but the going is rough, so any help here would go a long way. Thanks!!
NSFetchedResultsController may be overkill if all you want is to fetch an object and display one of its attributes in UILabel. Take a look at NSFetchRequest and start with something like this:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:entityDescription
inManagedObjectContext:managedObjectContext]];
NSPredicate *predicate = /* define predicate here */;
[fetchReqest setPredicate:predicate];
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
If you want to sort your results, read up on NSSortDescriptor. You will need to set sort descriptors prior to -executeFetchRequest: call.
Your results will be in 'results' array - and they should include NSManagedObjects that you can get attribute values from.

Resources