NSFetchedResultsController: ReOrder Sections - uitableview

I'm following the example from Apple to setup my sections:
https://developer.apple.com/library/ios/samplecode/DateSectionTitles/Listings/DateSectionTitles_APLEvent_m.html
My sections currently appear in the following order:
Section 0: "Upcoming"
Section 1: "Today"
Section 2: "Past"
Code I use in my NSManagedObject .m file:
#pragma mark - Transient properties
- (NSString *)sectionIdentifier
{
// Create and cache the section identifier on demand.
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp)
{
NSDate *dateToCompare = [self getUTCFormateDate:[self startDate]];
NSLog(#"********Date To Compare****** %#", dateToCompare);
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDate* now = [NSDate date];
NSDateFormatter *format = [[NSDateFormatter alloc] init];
format.dateFormat = #"dd-MM-yyyy";
NSString *stringDate = [format stringFromDate:now];
NSDate *todaysDate = [format dateFromString:stringDate];
NSInteger differenceInDays =
[calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:dateToCompare] -
[calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:todaysDate];
NSString *sectionString;
if (differenceInDays == 0)
{
sectionString = kSectionIDToday;
}
else if (differenceInDays < 0)
{
sectionString = kSectionIDPast;
}
else if (differenceInDays > 0)
{
sectionString = kSectionIDUpcoming;
}
tmp = sectionString;
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
-(NSDate *)getUTCFormateDate:(NSDate *)localDate
{
NSDateFormatter *dateFormatter;
if (!dateFormatter)
{
dateFormatter = [[NSDateFormatter alloc] init];
}
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
[dateFormatter setTimeZone:timeZone];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSString *dateString = [dateFormatter stringFromDate:localDate];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:dateString];
return dateFromString;
}
#pragma mark - Time stamp setter
- (void)setStartDate:(NSDate *)newDate
{
// If the time stamp changes, the section identifier become invalid.
[self willChangeValueForKey:#"startDate"];
[self setPrimitiveStartDate:newDate];
[self didChangeValueForKey:#"startDate"];
[self setPrimitiveSectionIdentifier:nil];
}
#pragma mark - Key path dependencies
+ (NSSet *)keyPathsForValuesAffectingSectionIdentifier
{
// If the value of timeStamp changes, the section identifier may change as well.
return [NSSet setWithObject:#"startDate"];
}
In my tableViewController, I setup the NSFetchedResults as following:
- (NSFetchedResultsController *)fetchedResultsController
{
if(_fetchedResultsController!=nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *firstSort = [[NSSortDescriptor alloc] initWithKey:#"startDate"
ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:firstSort,nil];
[fetchRequest setSortDescriptors:sortDescriptors];
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sectionIdentifier"
cacheName:nil];
self.fetchedResultsController.delegate = self;
return self.fetchedResultsController;
}
Question 1: How do I get the sections to appear in the following order:
Section 0: Today
Section 1: Upcoming
Section 2: Past
Question 2: Within each section, how do I sort the rows based on an attribute called "modified" in each object?

Both section and row ordering is 100% dependent upon the sort descriptors. You want your first sort descriptor to sort everything into the proper section and then your following sort descriptors will sort the rows within the sections.
For example, if you wanted three sections based off of "group" and then you wanted the rows sorted by name inside of the group you would add the sort descriptors as:
NSArray *descriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"group" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
[fetchRequest setSortDescriptors:descriptors];
Your section key for your NSFetchedResultsController will also need to match your first NSSortDescriptor.

Related

Performing Core Data Operations in self created multiple threads to save processing time

I have surfed a lot about performing core data operations in multiple threads but no good luck to solve my problem.
My code is such that I have to download a csv file after every ten minutes which contains each entry of 10 seconds. This file once downloaded is parsed and the contents are saved in database and then files are removed as then, when needed, I can fetch data from database.
Now, I have a huge existing content of more than a month for now which may extend to years also as time passes by, performing this huge task of saving new files to database and fetching objects from core data into an array for already downloaded files using a single thread is causing a huge processing time. Also, views in app needs to be adjusted with all previous data (They are basically plots of quantity vs time).
How can I achieve this in multiple threads and optimize my code processing time and reduce UI Blockage to minimum?
Please note that : Performing the task in background thread is not my concern as I in any case have to show graphs on the basis of total data. Please provide valuable advices.
EDIT: PERFORMED SOME MULTITHREADING
HERE IS THE NEW CODE,
- (void) downloadFiles:(NSString *)dataPath{
__block AppDelegate *appD = (AppDelegate *)[[UIApplication sharedApplication] delegate];
backgroundMOC = [[NSManagedObjectContext alloc] init];
[backgroundMOC setPersistentStoreCoordinator:[[appD managedObjectContext] persistentStoreCoordinator]];
if (parsedDetailsDataArrayForCurrentDay) {
parsedDetailsDataArrayForCurrentDay = nil;
}
if (parsedDetailsDataArrayForCurrentMonth) {
parsedDetailsDataArrayForCurrentMonth = nil;
}
if (parsedDetailsDataArrayForCurrentYear) {
parsedDetailsDataArrayForCurrentYear = nil;
}
parsedDetailsDataArrayForCurrentDay = [[NSMutableArray alloc] init];
parsedDetailsDataArrayForCurrentMonth = [[NSMutableArray alloc] init];
parsedDetailsDataArrayForCurrentYear = [[NSMutableArray alloc] init];
dispatch_group_t d_group = dispatch_group_create();
for (NSInteger i = 0; i <= (self.filesListArray.count - 1); i++) {
NSString *filePathOnPhone = [dataPath stringByAppendingString:[NSString stringWithFormat:#"/%#", [[self.filesListArray objectAtIndex:i] objectForKey:#"kCFFTPResourceName"]]];
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"ParsedInfiDetails"];
NSString *nameToGet = [[self.filesListArray objectAtIndex:i] objectForKey:#"kCFFTPResourceName"];
NSLog(#"File Check: %#", nameToGet);
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"fileName = %#", nameToGet];
[fetch setPredicate:predicate];
NSError *error = nil;
NSArray *results = [backgroundMOC executeFetchRequest:fetch error:&error];
NSArray *definedResults = [results copy];
if(definedResults && (definedResults.count !=0)) {
NSLog(#"Entities with that name: %#", results);
#autoreleasepool {
NSArray *result = [[definedResults sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modDate" ascending:YES]]] copy];
NSDate *specificDate = [NSDate date];
NSMutableArray *sortedDateArray = [[NSMutableArray alloc] init];
NSMutableArray *sortedDateCurrentYearArray = [[NSMutableArray alloc] init];
NSMutableArray *sortedDateCurrentMonthArray = [[NSMutableArray alloc] init];
for (int i = 0; i < result.count; i++) {
NSManagedObject *obj = [result objectAtIndex:i];
NSDate *objDate = [obj valueForKey:#"modDate"];
NSCalendar *gregorian = [NSCalendar currentCalendar];
NSDateComponents *components = [gregorian componentsInTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"] fromDate:objDate];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
NSDateComponents *specificComps = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:specificDate];
NSInteger specificDay = [specificComps day];
NSInteger specificMonth = [specificComps month];
NSInteger specificYear = [specificComps year];
if(day == 24){
}
if (day == specificDay && month == specificMonth && year == (specificYear-2000)) {
[sortedDateArray addObject:obj];
}
NSDate *todayDate = [NSDate date];
NSDateComponents *componentsForToday = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:todayDate];
NSInteger currentMonth = [componentsForToday month];
NSInteger currentYear = [componentsForToday year];
if (year == (currentYear-2000)) {
[sortedDateCurrentYearArray addObject:obj];
}
if (year == (currentYear -2000) && month == currentMonth) {
[sortedDateCurrentMonthArray addObject:obj];
}
}
NSMutableArray *sortedTimedArray = [[NSMutableArray alloc] initWithArray:[sortedDateArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
NSMutableArray *sortedTimedCurrentYearArray = [[NSMutableArray alloc] initWithArray:[sortedDateCurrentYearArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
NSMutableArray *sortedTimedCurrentMonthArray = [[NSMutableArray alloc] initWithArray:[sortedDateCurrentMonthArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
[parsedDetailsDataArrayForCurrentDay addObjectsFromArray:sortedTimedArray];
[parsedDetailsDataArrayForCurrentYear addObjectsFromArray:sortedTimedCurrentYearArray];
[parsedDetailsDataArrayForCurrentMonth addObjectsFromArray:sortedTimedCurrentMonthArray];
}
} else {
NSLog(#"Error: %#", error);
NSString *threadName = [NSString stringWithFormat:#"%ld THREAD", (long)i];
dispatch_queue_t myQueue = dispatch_queue_create([threadName UTF8String], NULL);
dispatch_group_async(d_group, myQueue, ^{
NSManagedObjectContext *backgroundMOC1;
backgroundMOC1 = [[NSManagedObjectContext alloc] init];
[backgroundMOC1 setPersistentStoreCoordinator:[[appD managedObjectContext] persistentStoreCoordinator]];
NSLog(#"Entered Thread ");
BOOL success = [appD.ftpManager downloadFile:[[self.filesListArray objectAtIndex:i] objectForKey:#"kCFFTPResourceName"] toDirectory:[NSURL URLWithString:dataPath] fromServer:srv];
if (success) {
// dispatch_group_async(d_group, myQueue, ^{
NSMutableDictionary *dict = [appD.ftpManager progress];
NSString *filePath = [dataPath stringByAppendingString:[NSString stringWithFormat:#"/%#", [[self.filesListArray objectAtIndex:i] objectForKey:#"kCFFTPResourceName"]]];
CHCSVParser *parser = [[CHCSVParser alloc] initWithContentsOfCSVURL:[NSURL fileURLWithPath:filePath]];
[parser parse];
NSMutableArray *currentFileComponentsArray = [NSArray arrayWithContentsOfCSVURL:[NSURL fileURLWithPath:filePath]];
NSMutableArray *parsedDetailsEntitiesArray = [[NSMutableArray alloc] init];
for (int j = 1; j <= (currentFileComponentsArray.count-1); j++) {
NSArray *detailsArray = [currentFileComponentsArray objectAtIndex:j];
if (!(detailsArray.count < 32)) {
NSManagedObject *parsedDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"ParsedInfiDetails"
inManagedObjectContext:[appD managedObjectContext]];
NSString *totalDateString = [NSString stringWithFormat:#"%# %#", [detailsArray objectAtIndex:0], [detailsArray objectAtIndex:1]];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"dd/MM/yyyy HH:mm:ss";
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
[dateFormatter setTimeZone:gmt];
NSDate *startDate = [dateFormatter dateFromString:totalDateString];
[parsedDetails setValue:startDate forKey:#"modDate"];
[parsedDetails setValue:startDate forKey:#"modTime"];
———————————————————————PERFORM PARSEDDETAILS STATEMENTS----------------
NSError *error;
NSLog(#"Saved File in Database: %#", [[self.filesListArray objectAtIndex:i] objectForKey:#"kCFFTPResourceName"]);
NSLog(#"Saved thread");
if (![backgroundMOC1 save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
else{
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
//if the download fails, we try to delete the empty file created by the stream.
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
//when data is stored in coredata remove the downloaded file.
}
[parsedDetailsEntitiesArray addObject:parsedDetails];
}
}
}
NSSet *set = [[NSSet alloc] initWithArray:parsedDetailsEntitiesArray];
[self.relevantInverId setValue:set forKey:#"infiDetails"];
NSDate *specificDate = [NSDate date];
#autoreleasepool {
NSMutableArray *sortedDateArray = [[NSMutableArray alloc] init];
NSMutableArray *sortedDateCurrentYearArray = [[NSMutableArray alloc] init];
NSMutableArray *sortedDateCurrentMonthArray = [[NSMutableArray alloc] init];
for (int i = 0; i < parsedDetailsEntitiesArray.count; i++) {
NSManagedObject *obj = [parsedDetailsEntitiesArray objectAtIndex:i];
NSDate *objDate = [obj valueForKey:#"modDate"];
NSCalendar *gregorian = [NSCalendar currentCalendar];
NSDateComponents *components = [gregorian componentsInTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"] fromDate:objDate];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
NSDateComponents *specificComps = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:specificDate];
NSInteger specificDay = [specificComps day];
NSInteger specificMonth = [specificComps month];
NSInteger specificYear = [specificComps year];
if(day == 24){
}
if (day == specificDay && month == specificMonth && year == (specificYear-2000)) {
[sortedDateArray addObject:obj];
}
NSDate *todayDate = [NSDate date];
NSDateComponents *componentsForToday = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:todayDate];
NSInteger currentMonth = [componentsForToday month];
NSInteger currentYear = [componentsForToday year];
if (year == (currentYear-2000)) {
[sortedDateCurrentYearArray addObject:obj];
}
if (year == (currentYear-2000) && month == currentMonth) {
[sortedDateCurrentMonthArray addObject:obj];
}
}
NSMutableArray *sortedTimedArray = [[NSMutableArray alloc] initWithArray:[sortedDateArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
[parsedDetailsDataArrayForCurrentDay addObjectsFromArray:sortedTimedArray];
NSMutableArray *sortedTimedCurrentYearArray = [[NSMutableArray alloc] initWithArray:[sortedDateCurrentYearArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
NSMutableArray *sortedTimedCurrentMonthArray = [[NSMutableArray alloc] initWithArray:[sortedDateCurrentMonthArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
[parsedDetailsDataArrayForCurrentYear addObjectsFromArray:sortedTimedCurrentYearArray];
[parsedDetailsDataArrayForCurrentMonth addObjectsFromArray:sortedTimedCurrentMonthArray];
}
// });
}
});
}
BOOL isFileAlreadyPresent = [[NSFileManager defaultManager] fileExistsAtPath:filePathOnPhone];
}
NSMutableArray *sortedParsedDetailsArrayForCurrentDay = [[NSMutableArray alloc] initWithArray:[parsedDetailsDataArrayForCurrentDay sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"modTime" ascending:YES]]]];
NSDate *startDate ;
NSDate *endaDate;
dispatch_group_notify(d_group, dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self stopLogoSpin];
[hud dismiss];
});
NSLog(#"All background tasks are done!!");
});
}
Now, logs #"Entered Thread " is visible in log but, #"Saved File in Database: %#", [[self.filesListArray objectAtIndex:i] and #"Saved thread" are not called.
Also,
BOOL success = [appD.ftpManager downloadFile:[[self.filesListArray objectAtIndex:i] objectForKey:#"kCFFTPResourceName"] toDirectory:[NSURL URLWithString:dataPath] fromServer:srv];
helps download file with their seperate thread in the download method.
Should uncommenting below help?
// dispatch_group_async(d_group, myQueue, ^{
Main thread is intentionally made to wait but this block below is also never called:
dispatch_group_notify(d_group, dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self stopLogoSpin];
[radialHUD dismiss];
});
NSLog(#"All background tasks are done!!");
});
Please advice any modification and solution to problems above.
Optimised the code by moving all what is not needed in loop outside and the performing the looping operation using GCD.
dispatch_queue_t myOwnQueue = dispatch_queue_create([#"MyOwnQueue" UTF8String], NULL);
dispatch_apply(self.filesListArray.count, myOwnQueue, ^(size_t i) { });
This lead to reduction in time by half. However any further optimisations need to be seen.

Display Old messages of tableview using fetchedResultsController delegate at Top instead of bottom When Request Old Data

Currently I've a Tableview that when it loads it makes a request to Core-Data and grabs the first 10 Messages of the newer ones.
// Initialize Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Chat"];
// Add Sort Descriptors
fetchRequest.fetchLimit = 10;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"room = %#", _roomid];
[fetchRequest setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"date_message" ascending:NO]]]; // YES;
// Initialize Fetched Results Controller
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
// Perform Fetch
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
After I call them. Messages are shown properly under. But When I load (fetch older messages on demand) I use the following.
- (void)fetchOldMessages {
[self.fetchedResultsController.fetchRequest setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"date_message" ascending:NO]]];
_fetchedResultsController.fetchRequest.fetchLimit +=10;
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
[self.tableView reloadData];
}
The issue is that the Old messages are now being shown below instead of above the new messages. Also new inserted messages are shown at the top of the tableView instead of the bottom.
this is my IndexPath:
- (UITableViewCell*)tableView:(UITableView*)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Chat *messageInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *CellIdentifier = #"fromMe";
FromMeTableViewCell *cell = [table dequeueReusableCellWithIdentifier:CellIdentifier];
cell.message_me.clipsToBounds = YES;
cell.message_me.layer.cornerRadius = 15;
cell.message_me.textContainerInset = UIEdgeInsetsMake(8.0, 8.0, 8.0, 8.0);
cell.message_me.text = messageInfo.message;
cell.message_date_me.text = [NSString stringWithFormat:#"%#", messageInfo.date_message];
cell.avatar_message_me.clipsToBounds = YES;
cell.avatar_message_me.layer.cornerRadius = cell.avatar_message_me.frame.size.width / 2;
cell.avatar_message_me.image = user_images[#"me"];
cell.message_me.backgroundColor = [UIColor colorWithRed:0.098 green:0.737 blue:0.611 alpha:1];
cell.message_me.textColor = [UIColor whiteColor]; //[UIColor whiteColor];
// Date Settings
NSDate *today = [[NSDate alloc]init];
NSDate *message_date = messageInfo.date_message;
NSDateFormatter *formatDate = [[NSDateFormatter alloc]init];
[formatDate setDateFormat:#"yyyy-MM-dd"];
NSString *today_string = [formatDate stringFromDate:today];
NSString *message_date_string = [formatDate stringFromDate:message_date];
// Make Date validation case is older display full date
if([today_string isEqualToString:message_date_string]){
NSDateFormatter *timeformat = [[NSDateFormatter alloc] init];
[timeformat setDateFormat:#"hh:mm a"];
NSDate *message_date = messageInfo.date_message;
NSString *string_date = [timeformat stringFromDate:message_date];
cell.message_date_me.text = [NSString stringWithFormat:#"Today at %#",[NSString stringWithFormat:#"%#",string_date]];
}
else {
NSDateFormatter *timeformat = [[NSDateFormatter alloc] init];
[timeformat setDateFormat:#"EEEE d hh:mm a"];
NSDate *message_date = messageInfo.date_message;
NSString *string_date = [timeformat stringFromDate:message_date];
cell.message_date_me.text = [NSString stringWithFormat:#"%#",[NSString stringWithFormat:#"%#",string_date]];
}
return cell;
}
This is when It first loads shows 5 Messages But the last message should be down ant not Above as you can see on the DATE
When I load more Data it shows the Messages in the right Order from new to older but the issue is that I want to show newer messages Below and Older messages Above.
Make your fetch request controller sorting ASCENDING to YES
[self.fetchedResultsController.fetchRequest
setSortDescriptors:#[[NSSortDescriptor
sortDescriptorWithKey:#"date_message" ascending:YES]]];

NSPredicate between two dates returns 0 results

I am trying to retrieve some results based on the "begin" (NSDate) field. However, it always returns 0 results.
And I can't figure out what I am doing wrong. I am guessing it has something to do with the NSDate type but I have double checked and everything seems correct.
I have checked this answer, and this one also. But to no avail…
Any help would be greatly appreciated.
Here is the definition in the model file :
#property (nonatomic, retain) NSDate * begin;
And here is the code from which I call the fetch Request.
-(void)getCalendarListForDatesFrom:(NSDate *)startDate
To:(NSDate *)endDate
inViews:(NSArray *)daysArray
{
// Reset dates to Midnight
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSUInteger preservedComponents = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit);
startDate = [calendar dateFromComponents:[calendar components:preservedComponents
fromDate:startDate ]
];
endDate = [calendar dateFromComponents:[calendar components:preservedComponents
fromDate:endDate ]
];
NSLog(#"Get Events for dates %# to %#", startDate, endDate);
_isRetrievingData = YES;
NSManagedObjectContext *MOC;
NSPredicate *predicate;
NSError *error = nil;
NSString *entityName = #"CalendarEvent";
//AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//BOOL doIHaveConnexion = [appDelegate getConnexion];
MOC = self.managedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
inManagedObjectContext:MOC ];
// Fetch Request
NSFetchRequest *fetchRequest;
fetchRequest = [[NSFetchRequest alloc] init];
if (!fetchRequest)
{
fetchRequest = [[NSFetchRequest alloc] init];
}
[fetchRequest setEntity:entity];
// Filter
NSLog(#"Filter results between dates %# and %#", startDate, endDate);
predicate = [NSPredicate predicateWithFormat:#"(%K >= %#) AND (%K <= %#)", #"begin", startDate, #"begin", endDate];
//predicate = [NSPredicate predicateWithFormat:#"(%K > %#)", #"begin", [NSDate date]];
NSLog(#"PREDICATE = %#", predicate);
NSLog(#"Set Predicate");
[fetchRequest setPredicate:predicate];
NSLog(#"Set Sort Descriptor");
NSSortDescriptor *beginDateDescriptor = [[NSSortDescriptor alloc] initWithKey:#"begin"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)
];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:beginDateDescriptor, nil];
NSLog(#"Fetch");
[fetchRequest setSortDescriptors:sortDescriptors];
NSLog(#"Init Fetch");
// Initialize Fetched Results Controller
if (!self.fetchedResultsController)
{
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:MOC
sectionNameKeyPath:nil
cacheName:nil ];
}
NSLog(#"Perform Fetch");
// Perform Fetch
[self.fetchedResultsController performFetch:&error];
NSArray *calEvents = [MOC executeFetchRequest:fetchRequest error:&error];
currentCalEvents = [calEvents mutableCopy];
if (error)
{
NSLog(#"Unable to execute fetch request.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
else
{
NSLog(#"TOTAL CALENDAR EVENTS %lu",(unsigned long)[calEvents count]);
NSLog(#"currentCalEvents = %#", currentCalEvents);
[self addCalendarEventsToDays: daysArray];
}
fetchRequest = nil;
}
Here are the logs from the console :
Filter results between dates 2015-01-02 23:00:00 +0000 and 2015-01-07 23:00:00 +0000
PREDICATE = begin >= CAST(441932400.000000, "NSDate") AND begin <= CAST(442364400.000000, "NSDate")
Set Predicate
Set Sort Descriptor
Fetch
Init Fetch
Perform Fetch
TOTAL CALENDAR EVENTS 0

Core data objects update through loop

I have a tableView showing core-data objects. On the same view there are five buttons. Each button action should update the value of an attribute from the objects.
As an example, I will show you what I have to update the attribute 'isDone':
- (IBAction)allDoneAction:(id)sender {
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
int i=0;
for (NSManagedObject *mo in context)
{
[mo setValue:#"Done" forKey:#"isDone"];i++;
}
[managedObjectContext save:nil];
}
This method throws following following exception:
NSManagedObjectContext countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x9a6b0a0
2014-01-06 19:01:43.862 To-Do Pro[679:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObjectContext countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x9a6b0a0'
What do I need to avoid the exception and obtain the desired update?
Here is my NSFetchedResultsController:
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController) return fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"FavoriteThing"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"displayOrder"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]
initWithObjects:sortDescriptor, nil];
//SOLO TO-DOS DE TODAY
todayDate = [NSDate date];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:todayDate]; // Get necessary date components
NSNumber *yearBuscado = [NSNumber numberWithLong:[components year]];
NSNumber *mesBuscado = [NSNumber numberWithLong:[components month]];
NSNumber *diaBuscado = [NSNumber numberWithLong:[components day]];
// NSString *tipourgente = #"Urgent";
// NSString *tipocolor = #"Yellow";
NSString *textoNotDone = #"Not done";
NSString *textoNotDeleted = #"Not deleted";
NSPredicate *yearPredicate = [NSPredicate predicateWithFormat:#"todoYear == %#", yearBuscado];
NSPredicate *monthPredicate = [NSPredicate predicateWithFormat:#"todoMonth == %#", mesBuscado];
NSPredicate *dayPredicate = [NSPredicate predicateWithFormat:#"todoDay == %#", diaBuscado];
NSPredicate *notDonePredicate = [NSPredicate predicateWithFormat:#"isDone== %#", textoNotDone];
NSPredicate *notDeletedPredicate = [NSPredicate predicateWithFormat:#"isSemiDeleted==%#", textoNotDeleted];
// NSPredicate *urgentPredicate = [NSPredicate predicateWithFormat:#"urgent == %#", tipourgente];
// NSPredicate *colorPredicate = [NSPredicate predicateWithFormat:#"color == %#", tipocolor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *busqueda = [NSCompoundPredicate andPredicateWithSubpredicates:#[yearPredicate,monthPredicate,dayPredicate,notDonePredicate,notDeletedPredicate]];
[fetchRequest setPredicate:busqueda];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
[self setFetchedResultsController:aFetchedResultsController];
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
Ok, I know absolutely nothing about core data, but from what I have researched in the last two minutes it appears as though you do not loop over An object of NSManagedObjectContext. You need to create a search within the context and get the results from that. Then iterate through the results and modify willy nilly.
Here is an example taken from this answer:
NSManagedObjectContext * context = [self managedObjectContext];
NSFetchRequest * fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:[NSEntityDescription entityForName:#"ShoppingBasket" inManagedObjectContext:context]];
NSArray * result = [context executeFetchRequest:fetch error:nil];
for (id basket in result)
[context deleteObject:basket];
So get the context, Create a search, get an array from the context based on the search criteria, loop through the results and do as you will with updating them.
If you are using a fetchedResultsController already to populate the tableView then you can iterate over the objects in the fetchedResultsController like this:
- (IBAction)allDoneAction:(id)sender {
NSArray *objects = [fetchedResultsController fetchedObjects];
for (NSManagedObject *mo in objects) {
[mo setValue:#"Done" forKey:#"isDone"];i++;
}
NSError *error;
bool result = [[fetchedResultsController managedObjectContext] save:&error];
if (!result) {
NSLog(#" error saving context, %#, %#", error, error.userInfo);
}
}
BTW you should be checking for errors in your call to save so don't pass in nil.

Using UICollectionReusableView to group Cells

I'm having trouble updating my old code which made synchronous JSOn calls to a new one which makes asynchronous calls using AFNetworking.
In my old code I was grouping cells with a UICollectionReusableView using a date string ("release_date"), all of this was done in the viewDidLoad. Now with AFNetworking I moved everything out of the viewDidLoad, so I'm stuck trying to figure out how to merge my old code with my new one.
This is the new code I have to parse my JSON with AFNetworking:
- (void)viewDidLoad
{
[super viewDidLoad];
self.upcomingReleases = [[NSMutableArray alloc] init];
[self makeReleasesRequests];
[self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:#"ReleaseCell"];
}
-(void)makeReleasesRequests //AFNetworking Call
{
NSURL *url = [NSURL URLWithString:#"http://www.soleresource.com/upcoming.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"#");
self.upcomingReleases = [responseObject objectForKey:#"upcoming_releases"];
[self.collectionView reloadData];
} failure:nil];
[operation start];
}
Code I had in my viewDidLoad before I started using AFNetworking to make JSON calls and "group" my cells:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *upcomingReleaseURL = [NSURL URLWithString:#"http://www.soleresource.com/upcoming.json"];
NSData *jsonData = [NSData dataWithContentsOfURL:upcomingReleaseURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSArray *upcomingReleasesArray = [dataDictionary objectForKey:#"upcoming_releases"];
//This is the dateFormatter we'll need to parse the release dates
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]]; //A bit of an overkill to avoid bugs on different locales
//Temp array where we'll store the unsorted bucket dates
NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
//We find the release date from the string
NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:#"release_date"]];
//We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
//This will represent our releases "bucket"
NSDate *bucket = [gregorian dateFromComponents:components];
//We get the existing objects in the bucket and update it with the latest addition
NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
if (!releasesInBucket){
releasesInBucket = [NSMutableArray array];
[unsortedReleaseWeek addObject:bucket];
}
UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:#"release_name"]];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
[releasesInBucket addObject:upcomingRelease];
[tmpDict setObject:releasesInBucket forKey:bucket];
}
[unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate* date1 = obj1;
NSDate* date2 = obj2;
//This will sort the dates in ascending order (earlier dates first)
return [date1 compare:date2];
//Use [date2 compare:date1] if you want an descending order
}];
self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
[self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:#"ReleaseCell"];
}
CollectionViewCell
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
ReleaseCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
// Part of my new code AFNetworking
NSDictionary *upcomingReleaseDictionary = [self.upcomingReleases objectAtIndex:indexPath.row];
//
// I had this in my old code
UpcomingRelease *upcomingRelease = [self.releaseWeekDictionary objectForKey:self.releaseWeek[indexPath.section]][indexPath.row];
//
cell.release_name.text = upcomingRelease.release_name;
return cell;
}
This is the rest:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return [self.releaseWeek count];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [[self.releaseWeekDictionary objectForKey:self.releaseWeek[section]] count];
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
ReleaseWeek *releaseWeek = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"releaseWeek" forIndexPath:indexPath];
//We tell the formatter to produce a date in the format "Name-of-the-month day"
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM dd"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]];
//We read the bucket date and feed it to the date formatter
NSDate *releaseWeekDate = self.releaseWeek[indexPath.section];
releaseWeek.releaseDate.text = [[dateFormatter stringFromDate:releaseWeekDate] uppercaseString];
return releaseWeek;
}
I'm basically trying to figure out how to take the code that grouped my cells be the date string and integrate it with my new code.
Thanks.
Think MVC. Separate your model (the stuff that comes from the network) into a separate class that does the network operations, and grouping things into arrays and dictionaries. Your view controller should simply observe the model. You can use delegation or (my favorite) KVO to know when the model has updated data available. Then you just update your collection view. Your view controller should simply be an interface between the model and the views. If you separate things out this way you will find that it is much more natural, and you aren't fighting against the system.
You are closer than you think.
Simply put everything you used to do in viewDidLoad: (everything between the assignment of jsonData to the registration of your collection view class) into the callback block of your AFNetworking call (where jsonData is now called responseObject).
At the end of the callback block, simply invoke [self.collectionView reloadData], and the your collection view will reload itself (i.e. call numberOfItems and cellForItemAtIndexPath for each item).
In your UICollectionViewDataSource methods that return the number of sections and items, simply return 0 if the properties that hold your model are nil or empty.
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
// Correctly returns 0 if nil or empty.
return [self.releaseWeek count];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if(!self.releaseWeekDictionary[section] || !self.releaseWeek[section]) {
return 0;
}else{
return [self.releaseWeekDictionary[self.releaseWeek[section]] count];
}
}
Below should be the gravy code in your completion block.
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"#");
NSArray *upcomingReleasesArray = [dataDictionary objectForKey:#"upcoming_releases"];
//This is the dateFormatter we'll need to parse the release dates
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]]; //A bit of an overkill to avoid bugs on different locales
//Temp array where we'll store the unsorted bucket dates
NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
//We find the release date from the string
NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:#"release_date"]];
//We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
//This will represent our releases "bucket"
NSDate *bucket = [gregorian dateFromComponents:components];
//We get the existing objects in the bucket and update it with the latest addition
NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
if (!releasesInBucket){
releasesInBucket = [NSMutableArray array];
[unsortedReleaseWeek addObject:bucket];
}
UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:#"release_name"]];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
[releasesInBucket addObject:upcomingRelease];
[tmpDict setObject:releasesInBucket forKey:bucket];
}
[unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate* date1 = obj1;
NSDate* date2 = obj2;
//This will sort the dates in ascending order (earlier dates first)
return [date1 compare:date2];
//Use [date2 compare:date1] if you want an descending order
}];
self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
[self.collectionView reloadData];
} failure:nil];
Edit: In your code, you were no longer assigning anything to upcomingReleases (block of commented code lines 91-102), and you were crashing referencing the index that didn't exist in the array. The fix is easy:
109: self.upcomingReleases = [dataDictionary objectForKey:#"upcoming_releases"];
122: for (NSDictionary *upcomingReleaseDictionary in self.upcomingReleases) {

Resources