I'm sorry for the long question; I have no idea how to explain this without including lots of code and images.
I am working on updating my app and because I discovered an issue relating to dates, I had to change an Attribute from NSDate
to NSString
. I'm using Core Data for my application.
The model of the application is:
Transaction Entity
Date Entity
Years Entity
The Year Entity has an attribute which is called yearOfEvent of type NSDate
and with the update to my app (already in the app store), I am changing the yearOfEvent to NSString
.
A bit of research and an answer to another question informed me that I have to do the migration myself.
This is mostly working, but is not quite there yet.
My application is a two-tabbed UITabBarController
where both Tabs are UITableViewControllers
. The first is a "Timeline" of chronological ordered entries. The second tab is supposed to display only the years that have been entered; so if a user has entries with 2014, 2013 and 2012, you'll see those in the cells in the Dates Tab and when you tap on that cell, it shows you the entries with that year. That's what I'm trying to change here. The app is populated by the user adding in some information into UITextFields
and picking a date from the UIDatePicker
which gets saved to Core-Data
and then the Table updates using NSFetchedResultsController
.
Problem
Right now, when I have my app from the App Store running with data and then migrate to the new version of my app with the custom migration, the Timeline is fine, but the cells of the Dates TableViewController have the labels showing blank years. The number of cells is correct, but the years are blank.
I followed the accepted answer in this question: Example or explanation of Core Data Migration with multiple passes? with the first bit of the code, but I'm not sure where to put the second part (final part of the code).
So in my Model class, I have:
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance
entityMapping:(NSEntityMapping *)mapping
manager:(NSMigrationManager *)manager
error:(NSError **)error
{
// Create a new object for the model context
NSManagedObject *newObject =
[NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName]
inManagedObjectContext:[manager destinationContext]];
// do our transfer of nsdate to nsstring
NSDate *date = self.transaction.dates.dateOfEvent;
//NSDate *date = [sInstance valueForKey:@"timeStamp"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"y"];
//NSString *stringFromYear = [formatter stringFromDate:trans.years.yearOfEvent];
// set the value for our new object
[newObject setValue:[dateFormatter stringFromDate:date] forKey:@"yearOfEvent"];
// do the coupling of old and new
[manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];
return YES;
}
So in my case, I'm extracting the "year" out of the Date Entity because the yearOfEvent is now a string.
Also, I have no idea where to place the rest of the code in the accepted answer with the migration.
For clarity, here's my NSFetchedResultsController which sets the dates:
- (NSFetchedResultsController *)fetchedResultsController
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Years" inManagedObjectContext:managedObjectContext];
fetchRequest.entity = entity;
NSPredicate *d = [NSPredicate predicateWithFormat:@"transactions.years.@count !=0"];
[fetchRequest setPredicate:d];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"yearOfEvent" ascending:NO];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
fetchRequest.fetchBatchSize = 20;
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
EnvylopeDatesTableViewCell *customCell = (EnvylopeDatesTableViewCell *)cell;
Years *years = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString *stringFromDate = years.yearOfEvent;
customCell.datesLabel.text = stringFromDate;
customCell.datesLabel.textColor = [UIColor whiteColor];
UIView *customColorView = [[UIView alloc] init];
customColorView.backgroundColor = [UIColor darkGrayColor];
customCell.selectedBackgroundView = customColorView;
}
In the MappingModel, I've left the default value expression for the YearstoYears entity and made sure to set the Custom Policy to be the Model class I created (Model.h/m).
Any guidance on this would be really appreciated.
Notes - The reason I have a Date and a Year tab was for formatting throughout the app. I'm sure I may be able to get away with having just a Date and then extracting the "year" out of that into an NSString. I will get to do that, but for now, I just want the migration to work appropriately.
- To reiterate, the problem is, if I am running an older version of the app and I migrate to this newly built version, the Dates tab does not correct set the years and the cells are blank for the year.