Query two and more song at one time using the function "addFilterPredicate" by MPMediaItemPropertyPersistentID - iphone

My app loads a music playlist automatically when it starts up. In order to do this, I store the song IDs MPMediaItemPropertyPersistentID to a database, and load the songs when the app is starting up next time. The main code is the following:
MPMediaQuery *MPMediaSongQuery = [MPMediaQuery songsQuery];
MPMediaPropertyPredicate *iPodMusicSongPredicateiPodMusicSongPredicate = [MPMediaPropertyPredicate
predicateWithValue:[NSNumber numberWithUnsignedLongLong: songID]
forProperty:MPMediaItemPropertyPersistentID
comparisonType:MPMediaPredicateComparisonEqualTo];
[MPMediaSongQuery addFilterPredicate:iPodMusicSongPredicate];
NSArray *collections = MPMediaSongQuery.collections;
The code loads song one by one. My question is: is there any way to query two or more songs by MPMediaItemPropertyPersistentID at one time when using the function addFilterPredicate:? Thanks.

If you use more than one addFilterPredicate they are combined with a logical AND. So you canm only reduce the results of the first query, but not extend it. As a consequence you are not allowed to use several addFilterPredicates for the same property. In fact the result is undefined and most likely will end up in an empty collection. What you are looking for is a combination of the same property with a logical OR. You can achieve this like depicted in the following pseudo-code:
MPMediaQuery *MPMediaSongQuery = [MPMediaQuery songsQuery];
NSMutableArray *collections = [[NSMutableArray alloc] initWithCapacity:1];
for (int i=0; i < songIDCount; i++) {
MPMediaPropertyPredicate *iPodMusicSongPredicateiPodMusicSongPredicate = [MPMediaPropertyPredicate
predicateWithValue:[NSNumber numberWithUnsignedLongLong: songID[i]]
forProperty:MPMediaItemPropertyPersistentID
comparisonType:MPMediaPredicateComparisonEqualTo];
[MPMediaSongQuery addFilterPredicate:iPodMusicSongPredicate];
[collections addObjectsFromArray:MPMediaSongQuery.collections];
[MPMediaSongQuery removeFilterPredicate:iPodMusicSongPredicate];
}
...

Related

Indexing and Sorting a UITableView with Values Parsed from an XML by Date

I am parsing an XML file from a URL, from this XML I parse titles of events, their dates, and locations of the events among other things. These elements are cached in an array called stories which acts as my data source for my table view. My goal is to display and indexed UITableView with the sections based on the date at which my events take place by month (January's events in a section, February's events in another, with month names as the titles for my sections). The events that will take place soonest will be at the top of my view, events further out should be at the bottom of my list, I don't have to worry about events that have already taken place. So far I've been able to display my event names and their dates as subtitles in my UITableView not sorted or indexed, left in the order by which they were parsed. When I log the stories array I view this in the console:
2013-01-04 00:17:22.332 NumberTwo[72213:c07] the stories array is as follows: (
{
additionalDesc = "";
allday = "Yes.";
endtime = "11:55 PM";
isodate = "2013-05-23";
location = "";
starttime = "12:00 AM";
title = "CST Exam Makeups";
},
{
additionalDesc = "";
allday = "No.";
endtime = "12:00 PM";
isodate = "2013-06-01";
location = "";
starttime = "8:00 AM";
title = "SAT & Subject Tests";
},)
There is an example of two elements in the array, I understand I am going to have break down the stories array into smaller ones based on the month that the events occur but I don't know what that method would look like. Here's my attempt at it in the viewDidLoad.
- (void)viewDidLoad {
[super viewDidLoad];
self.sections = [NSMutableDictionary dictionary];
for (UIEvent *event in stories)
{
NSDate *dateRepresentingThisDay = tempDate;
// If we don't yet have an array to hold the events for this day, create one
NSMutableArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
if (eventsOnThisDay == nil) {
eventsOnThisDay = [NSMutableArray array];
}
// Add the event to the list for this day
[eventsOnThisDay addObject:event];
}
// Create a sorted list of days
NSArray *unsortedDays = [self.sections allKeys];
self.sortedDays = [unsortedDays sortedArrayUsingSelector:#selector(compare:)];
}
I know the line for (UIEvent *event in stories) must be incorrect but I'm sure what should replace it. tempDate is a formatted date I receive from an NSDateFormatter in another method, and event is meant to refer to an element in my array stories. If you could, point me in the right direction to accomplish my goal, it would be much appreciated. Thank You.
For this you have to use the following steps:
1. On the number of section method of table view write the array count.
2. you also have to modify your array by comparing the dates which are available on stack overflow and arrange in array according to your requirement.
3. Set title by giving title.text = [your array object at index[index path of section]]
4. Now you can give rows as you like in it.
This are just steps you need to follow..
Have you got what I mean to say?
Is it possible for you create a class for the above data and have NSDate property to represent your start date/time of the event? If so, you can make use of NSPredicate to sort the array based on your start date property. You can find details on NSPredicate on this link.

Correct way to modify results array from NSFectchRequest without changing Core Data objects

I am retrieving a fetch from my core data database and trying to iterate through the data and make changes to the data, if necessary. When I change the data in the results array, it turns out that my database is changing in the back end as well, without doing a save. I am wondering what would be a good practice to use to change the data without affecting the back end data.
Here is the code I change the data with:
self.singleDayDataPointsForGraph = [[self fuelPurchaseDataForTimePeriodInMonths:self.numberOfMonthsForGraphView] mutableCopy];
for (int i = 0; i < self.singleDayDataPointsForGraph.count; i++) {
FuelPurchase *currentFuelPurchase = [self.singleDayDataPointsForGraph objectAtIndex:i];
if (i < self.singleDayDataPointsForGraph.count + 1 && self.singleDayDataPointsForGraph.count >= 2) {
FuelPurchase *purchaseToCompare = [self.singleDayDataPointsForGraph objectAtIndex:i + 1];
NSDate *firstDate = currentFuelPurchase.dateTimeStamp;
NSDate *secondDate = purchaseToCompare.dateTimeStamp;
NSDateFormatter *dateComparisonFormatter = [[NSDateFormatter alloc] init];
[dateComparisonFormatter setDateFormat:#"yyyy-MM-dd"];
if([[dateComparisonFormatter stringFromDate:firstDate] isEqualToString:[dateComparisonFormatter stringFromDate:secondDate]] ) {
float firstValue = [purchaseToCompare.fillSavings floatValue];
float secondValue = [currentFuelPurchase.fillSavings floatValue];
purchaseToCompare.fillSavings = [NSNumber numberWithFloat:(firstValue + secondValue)];
[self.singleDayDataPointsForGraph removeObjectAtIndex:i];
}
}
The fuelPurchaseDataForTimePeriodInMonths: method is what performs the fetch and returns an NSArray of results. self. singleDayDataPointsForGraph is an NSMutableArray that stores the results array as a mutable copy. This method basically checks two entries in the database to see if they are the on the same day and if they are, then it adds the fuel purchase amounts to each other and deletes one of the records. I don't want this to change my back end data, but it is.
Thanks very much.
It is doing the right thing. If you take a core data object and modify it, it will reflect immediately whether you save it ot not. The saving part ensures, that if you quit the application and come back the data is saved as well.
So for your situation, I would avoid modifying the actual core data object. Rather create a structure which imitates the core data object and modify that structure.
Example, say my coredata object is Person with attributes name and age.
The object a get from a fetch is say
person1.
You have a class PersonSub with same attributes.
Now you can create
PersonSub *personSub = [[PersonSub alloc] init]; //You can create a custom init to initilize from Person core data if you like.
personSub.name = person1.name;
personSub.age = person1.age;
Now you can modify as follows
personSub.age = personSub.age + 1;

Update original NSMutableArray after filtering with NSPredicate

I have recently started programming for the iOS Platform but now I need some help figuring out how to do 'something':
For my application I fetch some JSON data and put this data as objects into an Array
This Array is written to my own PLIST file (in the docs directory)
Now when the users starts a sync action I:
Fetch the data from the PLIST
Get the timestamp for a certain object in the Array that came from the PLIST
Use timestamp in new JSON request (for the new data)
So far so good.
Now for my (current) problem -> After receiving the new data (JSON req) I wish to update the timestamp of this 'certain' object in the array (and write this to the Plist).
Using an NSPredicate I am able to find the right set of data within the main Array (stampArr).
NSString *documentsDir = [NSHomeDirectory()stringByAppendingPathComponent:#"Documents"];
NSString *plistPath = [documentsDir stringByAppendingPathComponent:#"stamps.plist"];
NSMutableArray *stampArr = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"eventid = 1"];
NSMutableArray *filteredStampArr = [stampArr filteredArrayUsingPredicate:filter];
But now, after I update the filteredStampArr, I want to update the main Array with the data from the filtered Array.
In other words, I need to update the object from the Array with the new 'timestamp' (field of object).
I could off course use something like [stampArr addObject: [filteredStampArr copy]] after changing the filterd array but that would just create a duplicate of the information. I wish to overwrite the original object.
Somehow (I think) I need a 'pointer' that tells me the location of the data in the original array so that I can change the data directly in the main array?
(I hope my questions is clear - If not please say so)
Get the item, find it's index in stampArr and replace it with the newItem.
NSArray *filteredStampArr = [stampArr filteredArrayUsingPredicate:filter];
id item = [filteredStampArr objectAtIndex:0]; // id because the type of the item is not known
NSUInteger itemIndex = [stampArr indexOfObject:item];
[stampArr replaceObjectAtIndex:itemIndex withObject:newItem];
When you get filteredArray, you can directly update objects in it (not replace) and thay willbe uopdated in main array.
Read the API carefully!
try:
[stampArr filterUsingPredicate:];

Core data only storing last object of JSON feed

I´m using Core Data as local storage in my app. I´ve set it up properly and made subclasses of NSManagedObject for each entity. However, when I´m trying to insert values into my store, it only inserts the last object from my JSON feed.
res = [JSONHandler requestJSONResponse:jsonString];
shows = [res valueForKeyPath:#"Show.Name"];
NSUInteger showIndex = 0;
for(NSString *showName in shows){
showObject = [NSEntityDescription insertNewObjectForEntityForName:#"Show" inManagedObjectContext:managedObjectContext_];
showObject.name = showName;
showObject.iD = [[res valueForKeyPath:#"Show.Id"]objectAtIndex:showIndex];
showObject.desc = [[res valueForKeyPath:#"Show.Description"]objectAtIndex:showIndex];
showObject.activityType = [[res valueForKeyPath:#"Show.ActivityType"]objectAtIndex:showIndex];
showIndex++;
}
This only stores the last object from my JSON feed. Any idea why?
EDIT: It works fine when I do this:
res = [JSONHandler requestJSONResponse:jsonString];
shows = [res valueForKeyPath:#"Show.Name"];
NSUInteger index = 0;
for(NSString *showName in shows){
show = [NSEntityDescription insertNewObjectForEntityForName:#"Show" inManagedObjectContext:managedObjectContext_];
[show setValue:showName forKey:#"name"];
[show setValue:[[res valueForKeyPath:#"Show.Id"]objectAtIndex:index] forKey:#"iD"];
[show setValue:[[res valueForKeyPath:#"Show.Description"]objectAtIndex:index] forKey:#"desc"];
[show setValue:[[res valueForKeyPath:#"Show.ActivityType"]objectAtIndex:index] forKey:#"activityType"];
index++;
}
It´s basically the same thing, isn´t it? But I want to use subclasses of NSManagedObject instead of doing like I did above. Because in the snippet above show is NSManagedObject *show instead of what it should be: Show *show.
How many shows are there? You can find this by doing: NSLog(#"Number of shows: %d.", shows.count);, assuming that shows is an NSArray. It could be that your Core Data code is fine and the JSON parsing itself is at fault.
EDIT: Also, are you correctly saving the changes to the persistent store?
Usually when you see just one of several objects being saved like this, the problem is that a relationship that should be to-many is improperly set as to-one. No matter how many objects you try to add to the relationship, only the last one is set because the relationship can hold only one value.
I think in this circumstance the problem is most likely in the code of the custom subclass instead of the data model itself given that the data model works with generic NSManagedObjects.

Bulk update & occasional insert (coredata) - Too slow

Update: Currently looking into NSSET's minusSet
links: Comparing Two Arrays
Hi guys,
Could benefit from your wisdom here..
I'm using Coredata in my app, on first launch I download a data file and insert over 500 objects (each with 60 attributes) - fast, no problem.
Each subsequent launch I download an updated version of the file, from which I need to update all existing objects' attributes (except maybe 5 attributes) and create new ones for items which have been added to the downloaded file.
So, first launch I get 500 objects.. say a week later my file now contains 507 items..
I create two arrays, one for existing and one for downloaded.
NSArray *peopleArrayDownloaded = [CoreDataHelper getObjectsFromContext:#"person" :#"person_id" :YES :managedObjectContextPeopleTemp];
NSArray *peopleArrayExisting = [CoreDataHelper getObjectsFromContext:#"person" :#"person_id" :YES :managedObjectContextPeople];
If the count of each array is equal then I just do this:
NSUInteger index = 0;
if ([peopleArrayExisting count] == [peopleArrayDownloaded count]) {
NSLog(#"Number of people downloaded is same as the number of people existing");
for (person *existingPerson in peopleArrayExisting) {
person *tempPerson = [peopleArrayDownloaded objectAtIndex:index];
// NSLog(#"Updating id: %# with id: %#",existingPerson.person_id,tempPerson.person_id);
// I have 60 attributes which I to update on each object, is there a quicker way other than overwriting existing?
index++;
}
} else {
NSLog(#"Number of people downloaded is different to number of players existing");
So now comes the slow part.
I end up using this (which is tooooo slow):
NSLog(#"Need people added to the league");
for (person *tempPerson in peopeArrayDownloaded) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person_id = %#",tempPerson.person_id];
// NSLog(#"Searching for existing person, person_id: %#",existingPerson.person_id);
NSArray *filteredArray = [peopleArrayExisting filteredArrayUsingPredicate:predicate];
if ([filteredArray count] == 0) {
NSLog(#"Couldn't find an existing person in the downloaded file. Adding..");
person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:#"person" inManagedObjectContext:managedObjectContextPeople];
Is there a way to generate a new array of index items referring to the additional items in my downloaded file?
Incidentally, on my tableViews I'm using NSFetchedResultsController so updating attributes will call [cell setNeedsDisplay];
.. about 60 times per cell, not a good thing and it can crash the app.
Thanks for reading :)
I'll begin by saying that I'm still new to using the Core Data framework, but my guess is that your problem lies in the for loop you've posted.
If you look at your loop, each time it executes it creates a new NSPredicate object and then filters your existing array looking for matches. On a small data set this technique would work with seemingly small performance losses; however, with your large data set you will end up spending a lot of time creating NSPredicate objects that only differ in the name you've provided. I would suggest that you look at how to create a single predicate and then use variable substitution to perform the search. For information about variable use in predicates check out: http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html#//apple_ref/doc/uid/TP40003174
As a side note, you may also consider how you've sorted your data and how you are performing the search operation. And another thing I noticed is that you don't release your NSPredicate object, so you're just tossing memory away too.