NSFetchedResultsController group into sections for every 4 hours - core-data

I'm attempting to group a series of NSManagedObjects into sections. Every section would represent a period of 4 hours within a day.
- (NSString *)sectionIdentifier
{
// Create and cache the section identifier on demand.
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp)
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit) fromDate:[self created]];
tmp = [NSString stringWithFormat:#"%.0f %d %d %d", round(components.hour % 4), components.day, components.month, components.year];
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
In the above code, found in the NSManagedObject, the transient property sectionIdentifier returns a string consisting of the period (of 4 hours) it is in, the day, month, and year of it's creation.
However, although the sort descriptor for the NSFetchedResultController's fetchRequest is set to sort the results in order of their creation date (newest objects at the bottom, oldest at the top), the fetch request is failing due to the section identifier not being ordered properly.
- (NSFetchedResultsController *) fetchedResultsController {
if (!_fetchedResultsController) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"PSSpace" inManagedObjectContext:self.managedObjectContext]];
[fetchRequest setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"created" ascending:YES]]];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"sectionIdentifier" cacheName:nil];
[_fetchedResultsController setDelegate:self];
}
return _fetchedResultsController;
}
CoreData: error: (NSFetchedResultsController) The fetched object at index 10 has an out of order section name '1 14 12 2013. Objects must be sorted by section name'
Please can you tell me how I can fix this issue with the ordering?
Thanks.

Try something like
if (!tmp) {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit) fromDate:[self created]];
NSUInteger _y = (components.year * 1000000);
NSUInteger _m = (components.month * 10000);
NSUInteger _d = (components.day * 100);
NSUInteger _h = round(components.hour / 4);
NSUInteger token = (_y + _m + _d + _h);
tmp = [NSString stringWithFormat:#"%lu", token];
[self setPrimitiveSectionIdentifier:tmp];
}

Related

Core Data: Not saving

I'm having trouble saving to one variable letsMeet.startTimeLabel. Right after selecting NSLog shows the correct Value, however, after I save to another variable (letsMeet.endTimeLabel), letsMeet.startTimeLabel changes to (NULL). Below is the code:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
letsMeet = (LetsMeet *) [NSEntityDescription insertNewObjectForEntityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext];
switch (actionSheet.tag)
{
case 1:
{
if (buttonIndex == 0)
{
UIDatePicker *startDatePicker = (UIDatePicker *)[actionSheet viewWithTag:kDatePickerTag1];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd"];
NSDate *selectedDate = [startDatePicker date];
NSDateFormatter *dayFormatter = [[NSDateFormatter alloc] init];
[dayFormatter setDateFormat:#"EEEE"];
NSDate *selectedDay= [startDatePicker date];
NSDateFormatter *monthFormatter = [[NSDateFormatter alloc] init];
[monthFormatter setDateFormat:#"MMMM"];
NSDate *selectedMonth = [startDatePicker date];
NSString *date = [[NSString alloc] initWithFormat:#"%#", [dateFormatter stringFromDate:selectedDate]];
DateLabel.text = date;
[letsMeet setDateLabel:date];
NSString *month = [[NSString alloc] initWithFormat:#"%#", [dayFormatter stringFromDate:selectedMonth]];
MonthLabel.text = month;
[letsMeet setMonthLabel:month];
NSString *day = [[NSString alloc] initWithFormat:#"%#", [monthFormatter stringFromDate:selectedDay]];
DayLabel.text = day;
[letsMeet setDateLabel:day];
NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];
[timeFormatter setDateFormat: #"h:mm a"];
NSDate *selectedStartTime = [startDatePicker date];
NSString *startTime = [[NSString alloc] initWithFormat:#"%#", [timeFormatter stringFromDate:selectedStartTime]];
StartTimeLabel.text = startTime;
[letsMeet setStartTimeLabel:startTime];
NSError *error = nil;
if (![managedObjectContext save:&error]){
NSLog(#"Error Saving");
}
}
NSLog (#"This is the StartTime after selecting %#", letsMeet.startTimeLabel);
}
break;
case 2:
{
if (buttonIndex == 0)
{
UIDatePicker *endTimePicker = (UIDatePicker *)[actionSheet viewWithTag:kDatePickerTag2];
NSDateFormatter *endTimeFormatter = [[NSDateFormatter alloc] init];
[endTimeFormatter setDateFormat: #"h:mm a"];
NSDate *endSelectedTime = [endTimePicker date];
NSString *endTime = [[NSString alloc] initWithFormat:#"%#", [endTimeFormatter stringFromDate:endSelectedTime]];
EndTimeLabel.text = endTime;
[letsMeet setEndTimeLabel:endTime];
NSLog (#"This is the EndTime %#", letsMeet.endTimeLabel);
NSLog (#"This is the StartTime after selecting BOTH %#", letsMeet.startTimeLabel);
}
else if (buttonIndex == 1)
{
EndTimeLabel.text = #"Whenever";
[letsMeet setEndTimeLabel:EndTimeLabel.text];
}
NSError *error = nil;
if (![managedObjectContext save:&error]) {
}
}break;
// Handle the error.
}
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
NSLog (#"Prepare For Segue StartTime %#", letsMeet.startTimeLabel);
NSLog (#"Prepare For Segue EndTime%#", letsMeet.endTimeLabel);
}
Here is the log:
2013-02-20 21:38:24.253 AppointmentTime[3129:c07] This is the StartTime after selecting 9:30 AM
2013-02-20 21:38:32.325 AppointmentTime[3129:c07] This is the EndTime 12:15 PM
2013-02-20 21:38:32.325 AppointmentTime[3129:c07] This is the StartTime after Selecting BOTH (null)
2013-02-20 21:38:34.069 AppointmentTime[3129:c07] Prepare For Segue StartTime (null)
2013-02-20 21:38:34.069 AppointmentTime[3129:c07] Prepare For Segue EndTime12:15 PM
Q: Why would letsMeet.startTimeLabel show up correct the first time and after selecting EndTime, it changes to NULL. Please note EndTime continues to show the correct Value all the way up to prepareForSegue. Weird!
According to your logs and code , you are entering the switch block twice. Which means you are entering the actionSheet:clickedButtonAtIndex: method twice. So each time you enter the method
letsMeet = (LetsMeet *) [NSEntityDescription insertNewObjectForEntityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext];
statement is executed twice, in turn creating two objects. You can see this by doing a fetch from the store.
So you are checking for properties in two different objects and hence the null.
If you are using just one managed object, you can probably add a check for nil for the object before executing insertNewObjectForEntityForName:inManagedObjectContext:. This will make sure you are using the same object.
If you are using more than one object at the same time use the object id or some unique key to identify your object and manipulate it.
Edit:
You can check for nil with the following code:
if(letsMeet==Nil){
letsMeet = (LetsMeet *) [NSEntityDescription insertNewObjectForEntityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext];
}
This will work only, if the object you are calling the actionSheet:clickedButtonAtIndex: method is always in memory. But since you are persisting you might want to fetch the object from the store and then check for no. of objects.
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext]];
NSArray *objectArray = [managedObjectContext executeFetchRequest:request error:&error]
if(objectArray.count==0){
letsMeet = (LetsMeet *) [NSEntityDescription insertNewObjectForEntityForName:#"LetsMeet" inManagedObjectContext:managedObjectContext];
}else{
letsMeet = (LetsMeet *)[objectArray objectAtIndex:0];
}
Note: If you need to persist only a couple of variables, core-data might be an overkill. Use NSUserDefaults instead and keep it simple.

Core Data Optimize Fetch Request

This Fetch Request takes almost 10 seconds to execute on iPhone.
The goal is to fetch 1 random thumbnail from each category.
setting fetchlimit = 1 most of the times return the same thumbnail so I have to fetch all photos from each category.
Any ideas ?
[categoriesArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Photos"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"place.subcategory.category == %#", object];
[request setPredicate:predicate];
NSError *error;
NSArray *photosManagedObjectsArray = [managedObjectContext executeFetchRequest:request error:&error];
NSUInteger randomIndex = arc4random() % [photosManagedObjectsArray count];
NSManagedObject *photoObject = [photosManagedObjectsArray objectAtIndex:randomIndex];
UIImage *photoImage = [UIImage imageWithData:[photoObject valueForKey:#"thumbnail"]];
UIImage *resizedImage = [photoImage imageCroppedToFitSize:CGSizeMake(IMAGE_WIDTH, IMAGE_HEIGHT)];
[imagesArray addObject:resizedImage];
[objectsArray addObject:photoObject];
}];
many thanks!
Bill.
You don't need to pull all your objects into an array. Just change
NSArray *photosManagedObjectsArray = [managedObjectContext executeFetchRequest:request error:&error];`
NSUInteger randomIndex = arc4random() % [photosManagedObjectsArray count];
to this
NSUInteger count = [managedObjectContext countForFetchRequest:request error:&error];
NSUInteger randomIndex = arc4random() % count;
Now use fetchOffset to grab just the single object you need.
[request setFetchOffset:randomIndex];
[request setFetchLimit:1];

Failing to add NSDate into NSDictionary

I am trying to addd my nsdate into nsdictionary.CAn anyone tell me how to add it?
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
//if(conditionCount ==0){
if( [#"forecast_information" isEqualToString:elementName] ) {
//conditionDate = get date
NSDate *now=[NSDate date];
isParsingInformation=YES;
NSArray *array=[NSArray arrayWithObject:now];
NSLog(#" the time is %#",array);
[forecastConditions addObject:[NSMutableDictionary dictionary]];
}
else if([#"forecast_date" isEqualToString:elementName])
{
if(!forecast_information)
forecast_information=[[NSMutableArray alloc]init];
}
else if(isParsingInformation){
NSMutableDictionary *field=[forecastConditions lastObject];
[field setObject:[attributeDict objectForKey:#"data"] forKey:elementName];
}
i dnt know..see what I actually want to do is I am getting my google weather api in an nsdictionary named fields..I want to add my NSDate from the system at the first index of nsdictionary..I NSdictionary I couple of data,I want to add my nSdate at the first index..I am not able to do it.
I am trying to increment by date by each loop...how to do it?
i think it is date not data
[field setObject:[attributeDict objectForKey:#"date"] forKey:elementName];
updated code
NSMutableDictionary *dic=[[NSMutableDictionary alloc] init];//creation
[dic setObject:[NSDate date] forKey:#"Today"];//added
NSLog(#"dic is : %# \n\n",dic);
NSDate *now = [NSDate date];
int daysToAdd = 50; // or 60 :-)
NSDate *newDate1 = [now addTimeInterval:60*60*24*daysToAdd];
NSLog(#"Quick: %#", newDate1);
OR
NSDate *now = [NSDate date];
int daysToAdd = 50; // or 60 :-)
// set up date components
NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
[components setDay:daysToAdd];
// create a calendar
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDate *newDate2 = [gregorian dateByAddingComponents:components toDate:now options:0];
NSLog(#"Clean: %#", newDate2);

Find assets in library - add to a AVMutableComposition - export = crash

I've been struggling with adding assets from the iPhone Photo Library to a AVMutableComposition and then export them. Here is what I got:
Finding the assets: (here I grab the AVURLAsset)
-(void) findAssets {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just videos.
[group setAssetsFilter:[ALAssetsFilter allVideos]];
[group enumerateAssetsUsingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop){
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
NSURL *url = [representation url];
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
// Do something interesting with the AV asset.
[thumbs addObject:alAsset];
[assets addObject:avAsset];
}else if(alAsset == nil){
[self createScroll];
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(#"No groups");
}];
[library release];
}
Here I add a asset to my composition (I use the first object in the array for testing only.
-(void) addToCompositionWithAsset:(AVURLAsset*)_asset{
NSError *editError = nil;
composition = [AVMutableComposition composition];
AVURLAsset* sourceAsset = [assets objectAtIndex:0];
Float64 inSeconds = 1.0;
Float64 outSeconds = 2.0;
// calculate time
CMTime inTime = CMTimeMakeWithSeconds(inSeconds, 600);
CMTime outTime = CMTimeMakeWithSeconds(outSeconds, 600);
CMTime duration = CMTimeSubtract(outTime, inTime);
CMTimeRange editRange = CMTimeRangeMake(inTime, duration);
[composition insertTimeRange:editRange ofAsset:sourceAsset atTime:composition.duration error:&editError];
if (!editError) {
CMTimeGetSeconds (composition.duration);
}
}
And finally I export the comp and here it crashes
-(void)exportComposition {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
NSLog (#"can export: %#", exportSession.supportedFileTypes);
NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [dirs objectAtIndex:0];
NSString *exportPath = [documentsDirectoryPath stringByAppendingPathComponent:EXPORT_NAME];
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
NSURL *exportURL = [NSURL fileURLWithPath:exportPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;//#"com.apple.quicktime-movie";
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog (#"i is in your block, exportin. status is %d",
exportSession.status);
switch (exportSession.status) {
case AVAssetExportSessionStatusFailed:
case AVAssetExportSessionStatusCompleted: {
[self performSelectorOnMainThread:#selector (exportDone:)
withObject:nil
waitUntilDone:NO];
break;
}
};
}];
}
Does anyone have an idea of what it might be? It crashes on
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetPassthrough];
And I tried different presets and outputFileTypes.
Thanks
* SOLVED *
I have to answer my own question now when I have solved. It's amazing that I've been struggling with this for a whole day and then I fix it right after posting a question :)
I changed and moved:
composition = [AVMutableComposition
composition];
to:
composition = [[AVMutableComposition
alloc] init];
I think I was too tired when I was working on this yesterday. Thanks guys!

Core Data complex query

I have model that have field date. I need to fetch all count of records for all day of year or if no record for this day presented fetch as 0. Now i fetch all record for year and make it by hands. Can I do it using core data queries?
Typically in Core Data it is much faster to do all the fetching you will need to do up front rather than breaking it up into multiple fetches.
I tried to count records by day of the year in two ways:
Fetch all the records for a year. Find distinct dates for those records. Loop over the dates and count the records.
Loop over each day in the year. Perform a count-only fetch for the records with that date.
With a small database (48 records), the first approach was about 90 times faster. I imagine the performance of the first approach would get worse as more records are added and the second approach would stay about the same.
Here is the code:
- (void)doFullLoadTestWithYear:(int)year {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Record" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *comps = [[[NSDateComponents alloc] init] autorelease];
[comps setDay:1];
[comps setMonth:1];
[comps setYear:year];
NSDate *start = [gregorian dateFromComponents:comps];
[comps setDay:31];
[comps setMonth:12];
NSDate *end = [gregorian dateFromComponents:comps];
NSPredicate *yearPred = [NSPredicate predicateWithFormat:#"date >= %# && date <= %#",start,end];
[fetchRequest setPredicate:yearPred];
NSError *error = nil;
NSArray *array = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
if (error) {
NSLog(#"Error during fetch: %#",[error localizedDescription]);
abort();
}
int dateCount = 0, recordCount = 0;
NSArray *dates = [array valueForKeyPath:#"#distinctUnionOfObjects.date"];
for (NSDate *date in dates) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"date == %#",date];
NSArray *records = [array filteredArrayUsingPredicate:pred];
dateCount++;
recordCount += [records count];
NSLog(#"%d record(s) with date %#",[records count],date);
}
NSLog(#"Record count for year is %d",recordCount);
NSLog(#"Distinct dates count is %d",dateCount);
}
- (void)doSeparateTestWithYear:(int)year {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Record" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *comps = [[[NSDateComponents alloc] init] autorelease];
[comps setDay:1];
[comps setMonth:1];
[comps setYear:year];
NSDate *start = [gregorian dateFromComponents:comps];
[comps setYear:year+1];
NSDate *end = [gregorian dateFromComponents:comps];
NSDateComponents *oneDay = [[[NSDateComponents alloc] init] autorelease];
[oneDay setDay:1];
int dateCount = 0, recordCount = 0;
NSDate *date = start;
while (![date isEqual:end]) {
NSPredicate *dayPred = [NSPredicate predicateWithFormat:#"date == %#",date];
[fetchRequest setPredicate:dayPred];
NSError *error = nil;
int count = [self.managedObjectContext countForFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Error during fetch: %#",[error localizedDescription]);
abort();
}
if (count > 0) {
NSLog(#"%d record(s) with date %#",count,date);
dateCount++;
recordCount += count;
}
date = [gregorian dateByAddingComponents:oneDay toDate:date options:0];
}
NSLog(#"Record count for year is %d",recordCount);
NSLog(#"Distinct dates count is %d",dateCount);
[fetchRequest release];
}

Resources