I have a controller that is the root of a workflow. If there is no data object for the workflow, then I create a new one, and if there is, I use the existing one. I have a property for the model object (an NSManagedObject)
#property (nonatomic, retain) Round *currentRound;
and I call the following whenever the corresponding view is shown
self.currentRound = [self.service findActiveRound];
if (!self.currentRound) {
NSLog((#"configure for new round"));
self.currentRound = [self.service createNewRound];
...
} else {
NSLog(#"configure for continue");
// bad data here
}
The problem is at the place marked in the above, sometimes the data object is corrupted. In the parts I didn't show I set the values on some text fields to represent the values in the model. Sometimes its ok, but eventually the properties on the model object are empty and things break
In the debugger, the reference to the round doesn't appear to change, but NSLogging the relevant properties shows them nullified. debugging seems to delay the onset of the corruption.
I am aware I am not saving the context...should that matter? And if so, how come it doesn't always fail the first time I come back to this controller?
My findActiveRound message is nothing special, but in case it matters
-(Round *) findActiveRound
{
NSLog(#"Create Active Round");
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Round" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"isComplete == %#", [NSNumber numberWithBool:NO]];
[request setPredicate:pred];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if ([results count] == 0) {
return nil;
} else {
return [results objectAtIndex:0];
}
}
Many thanx.
EDIT FOR RESPONSE
By corrupted I mean when I try to get some simple string properties off the model object, I get nil values. So In the code above (where I think I have a round) I do stuff like
self.roundName.text = self.currentRound.venue;
self.teeSelection.text = self.currentRound.tees;
and don't see the data I entered. Since it only fails sometimes, but always fails eventually, I will see the data I entered for a while before its gone.
I'm pretty sure the context is the same. My service is a singleton and created like so
#implementation HscService
+(id) getInstance
{
static HscService *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[self alloc] init];
});
return singleton;
}
-(id) init
{
if (self = [super init]) {
model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSString *path = [self itemArchivePath];
NSURL *storeUrl = [NSURL fileURLWithPath:path];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES],
NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
[NSException raise:#"Open failed" format: #"Reason: %#", [error localizedDescription]];
}
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:psc];
[context setUndoManager:nil];
}
return self;
}
-(NSString *) itemArchivePath
{
NSArray *docDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [docDirectories objectAtIndex:0];
return [dir stringByAppendingPathComponent:#"hsc1.data"];
}
and in every controller I get the singleton to perform operations. I plan on defining a delegate around round operations, and implementing it in my AppDelegate, so I'm only getting the service once in the app, but don't think that should matter for now....
Are you sure the data is actually corrupted? Managed object contexts are highly efficient, and it's normal to fault in the debugger. From the docs:
"Faulting is a mechanism Core Data employs to reduce your
application’s memory usage..."
See http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFaultingUniquing.html
If the data is actually missing and cannot be accessed by other methods, make sure you're using the same Managed Object Context to access the data. If the data has not been committed to the data store, it will not "sync" between MOCs.
Related
SO,
I have a problem with Core Data and trying to save data out to a SQLite database correctly.
I have two "Apps": one for loading the SQLite data (Lets call that "Loader.App"), and one for displaying the data ("Display.App"). The loader is just a convenience "bridge" between a web-based CMS which exports data as JSON data and the App which needs an SQLite DB (which is, again, loaded by Core Data).
When I save the context in Loader.App, it saves the data into the SQLite file. I can open this file in a SQLite reader (like Base.App) and it shows all the data. The problem is: when I bring that SQLite file to Display.App, it copies the file into the documents directory but it doesn't have any data inside of it. It does, however, have all of the proper tables - just like the SQLite file before I load the data.
The odd thing is that if I open the SQLite DB file in a reader (Base.App) and VACUUM the database, it loads in Display.App perfectly fine. From experience with file io in Python I know that if you don't close the file properly, the data isn't written to the file from the io buffer. Clearly, the data is being written to the SQLite file (thus I can open it with a reader (Base.App)). But it makes me wonder if there a file closing method that I am not calling?
Basically...
Method 1:
Run Loader.App
Copy MyAppDB.sqlite to Display.App
Run Display.App
Result: MyAppDB.sqlite has no data inside of it
Method 2:
Run Loader.App
Open MyAppDB.sqlite with reader (Base.App)
VACUUM
Copy MyAppDB.sqlite to Display.App
Run Display.App
Result: MyAppDB.sqlite contains data and we have much joy.
Here is a trimmed down version of my Loader.App:
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Create the managed object context
NSManagedObjectContext *context = managedObjectContext();
// Custom code here...
importDataEntriesFromJSON(context);
// Save the managed object context
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error while saving %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
exit(1);
}
}
return 0;
}
static NSManagedObjectModel *managedObjectModel() {
static NSManagedObjectModel *model = nil;
if (model != nil) {
return model;
}
NSString *path = #"MyAppDB";
path = [path stringByDeletingPathExtension];
NSURL *modelURL = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:#"mom"]];
model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return model;
}
static NSManagedObjectContext *managedObjectContext() {
static NSManagedObjectContext *context = nil;
if (context != nil) {
return context;
}
#autoreleasepool {
context = [[NSManagedObjectContext alloc] init];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel()];
[context setPersistentStoreCoordinator:coordinator];
NSString *STORE_TYPE = NSSQLiteStoreType;
NSString *path = #"MyAppDB";
path = [path stringByDeletingPathExtension];
NSURL *url = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:#"sqlite"]];
// Clear old SQLite
NSFileManager *manager = [NSFileManager defaultManager];
NSError *error;
[manager removeItemAtURL:url error:&error];
//NSError *error;
NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE configuration:nil URL:url options:nil error:&error];
if (newStore == nil) {
NSLog(#"Store Configuration Failure %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
}
}
return context;
}
void importDataEntriesFromJSON( NSManagedObjectContext *context ) {
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"data_entries" ofType:#"json"];
NSArray* data_entries = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported %lu data_entries from JSON", (unsigned long)[data_entries count]);
[data_entries enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
DBDataEntry *dataEntry = [NSEntityDescription
insertNewObjectForEntityForName:#"DBDataEntry"
inManagedObjectContext:context];
dataEntry.data_entry_id = [NSNumber numberWithInt:[[obj objectForKey:#"data_entry_id"] integerValue]];
dataEntry.data_entry_keywords = [obj objectForKey:#"data_entry_keywords"];
dataEntry.data_entry_name = [obj objectForKey:#"data_entry_name"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DBDataEntry"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for( DBDataEntry *dataEntry in fetchedObjects ) {
//NSLog(#"data_entry_id: %#", dataEntry.data_entry_id);
//NSLog(#"data_entry_keywords: %#", dataEntry.data_entry_keywords);
//NSLog(#"data_entry_name: %#", dataEntry.data_entry_name);
NSLog(#"data_entry_id: %# name: %#", dataEntry.data_entry_id, dataEntry.data_entry_name);
}
}
Thanks for your help! :)
The most likely reason is that you're copying the SQLite file itself but not its journal files. On iOS 7 Core Data normally uses SQLite in WAL (write-ahead logging) mode. That means that besides MyAppDB.sqlite there will be files named MyAppDB.sqlite-wal and MyAppDB.sqlite-shm. Those files are crucial. If you copy just the SQLite file but not the journals, you'll lose data (as you've seen).
When you open the SQLite file in Base.app and vacuum, all the changes in the journal files are rolled into the main SQLite file itself. You're doing an extra step that eliminates the need to copy the journal files.
You have a couple of different options:
The easy way is to just copy all of the files. Problem solved.
Another option is to change the journal mode in your loader app to avoid the need to copy more files. You'd do this by passing an extra option when adding the persistent store:
NSDictionary *options = #{ NSSQLitePragmasOption : #{ #"journal_mode": #"DELETE" } };
Use that when calling addPersistentStoreWithType:configuration:URL:options:error:.
I have a JSON request which returns different parameters, name for example. I would like to cache a variable for the name parameter, which I can view in the app later.
For example: name from JSON request = david. the variable is called firstname. firstname should equal "david". firstname should be stored so I can view it in all parts of my apps.
For something simple as a string, the quick and dirty solution is to store it in NSUserDefaults.
Storing
[[NSUserDefaults standardDefaults] setObject:firstname forKey:#"kUserFirstName"];
[[NSUserDefaults standardDefaults] synchronize];
Retrieving
NSString *string = [[NSUserDefaults standardDefaults] objectforKey:#"kUserFirstName"];
If it gets more complicated than that, you have to consider a more structured persistence store. A valid option is CoreData.
There exist a few frameworks that might help you in storing JSON resources in CoreData, the most interesting being RestKit.
First off, you might consider checking out RestKit as it successfully accomplishes a whole slew of server interaction and CoreData persistence in iOS.
I'm a little short on time (on a lunch break here) so I'll just lazily post an example from an app I have.
- (void)loadFiltersFromJSON {
NSString *path = [[NSBundle mainBundle] pathForResource:#"FreeFilterBank" ofType:#"json"];
NSData *filterData = [NSData dataWithContentsOfFile:path];
NSError *err;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:filterData options:kNilOptions error:&err];
if (err) { //TODO this can be removed prior to shipping the app
NSLog(#"%#", err);
}
NSArray *definedFilters = [json objectForKey:#"Filter"];
NSManagedObjectContext* moc = [self managedObjectContext];
for (NSDictionary *filter in definedFilters) {
NSString *name = [filter valueForKey:#"name"];
BOOL exists = [self alreadyExists:name inManagedObjectContext:moc];
if (!exists) {
NSString *imageNamed = [filter valueForKey:#"imageName"];
NSString *filterDesignator = [filter valueForKey:#"filterDesignator"];
NSString *paid = [filter valueForKey:#"paidOrFree"];
[self createFilterWithName:name imageNamed:imageNamed filterDesignator:filterDesignator paidOrFree:paid];
}
}
}
- (BOOL)alreadyExists:(NSString*)filterNamed inManagedObjectContext:(NSManagedObjectContext*)moc {
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"name == %#", filterNamed];
NSEntityDescription* description = [NSEntityDescription entityForName:#"Filter" inManagedObjectContext:moc];
NSFetchRequest* request = [[NSFetchRequest alloc] init];
[request setEntity:description];
[request setPredicate:predicate];
NSError* error;
NSArray* fetchedResult = [moc executeFetchRequest:request error:&error];
if (error) {
NSLog(#"%#",error.localizedDescription);
}
if (fetchedResult.count == 0) {
return NO;
}
else {
return YES;
}
}
- (void)createFilterWithName:(NSString*)name imageNamed:(NSString*)imageName filterDesignator:(NSString*)designator paidOrFree:(NSString *)paid {
NSManagedObjectContext* moc = [self managedObjectContext];
Filter* newFilter = [NSEntityDescription insertNewObjectForEntityForName:#"Filter" inManagedObjectContext:moc];
newFilter.name = name;
newFilter.imageName = imageName;
newFilter.filterDesignator = designator;
newFilter.paidOrFree = paid;
NSError* error;
[moc save:&error];
if (error) {
NSLog(#"%#",error.localizedDescription);
}
}
TL;DR This loads data from a JSON stored in the bundle, checks the SQLite data store to see if we already have something with the same name, and creates a new persistent instance of this object if we don't.
Take this example for what you will, there are many many more invocations for serialized data pulled from the web and persistent data within iOS beyond this one example.
The easiest way is to use NSUserDefaults and set the key to #"firstname" and the value would be #"david". That being said, you might consider using a better persistence model like CoreData. You can also use an Sqlite database or have the key/value saved in a plist. There are a number of ways to do this.
For reference ,see this:
Save string to the NSUserDefaults?
I'm trying to update a UITableView in a view called "HomeViewController" to show the new objects that I've added. But the NSFetchedResultsController doesn't update after I've finished creating a new one. I have the following code in various documents:
HomeViewController.h
- (void)saveNewShindyToDatabaseForName:(NSString *)name
details:(NSString *)details
dateAndTime:(NSDate *)dateAndTime
location:(CLLocation *)location
timePosted:(NSDate *)postedTime
{
if (!self.shindysDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
self.shindysDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
dispatch_queue_t saveQ = dispatch_queue_create("Shindy Creator", nil);
dispatch_async(saveQ, ^{
[self.shindysDatabase.managedObjectContext performBlock:^{
[Shindy setupShindyForSaveToDatabaseForName:name
details:details
dateAndTime:dateAndTime
location:location
timePosted:postedTime
inManagedObjectcontext:self.shindysDatabase.managedObjectContext];
}];
});
NSError *error = nil;
if (![self.shindysDatabase.managedObjectContext save:&error]) {
NSLog(#"%#", error.userInfo);
}
}
- (void)setupFetchedResultsController
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
NSSortDescriptor *nameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
NSSortDescriptor *photoSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"photo" ascending:YES];
NSSortDescriptor *detailSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
NSSortDescriptor *dateAndTimeSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateAndTime" ascending:YES];
NSSortDescriptor *timePostedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"timePosted" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObjects:nameSortDescriptor, photoSortDescriptor, detailSortDescriptor, dateAndTimeSortDescriptor, timePostedSortDescriptor, nil];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.shindysDatabase.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
}
Shindy+SQL.m (An NSManagedObject subclass of the entity, "Shindy")
+ (Shindy *)setupShindyForSaveToDatabaseForName:(NSString *)name
details:(NSString *)details
dateAndTime:(NSDate *)dateAndTime
location:(CLLocation *)location
timePosted:(NSDate *)postedTime
inManagedObjectcontext:(NSManagedObjectContext *)context
{
Shindy *shindy = [NSEntityDescription insertNewObjectForEntityForName:#"Shindy" inManagedObjectContext:context];
__block NSString *nameString = name;
__block FBProfilePictureView *profileImage = nil;
if (FBSession.activeSession.isOpen) {
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (!error) {
nameString = user.name;
profileImage.profileID = user.id;
}
}];
}
NSData *imageData = nil;
for (NSObject *obj in [profileImage subviews]) {
if ([obj isMemberOfClass:[UIImageView class]]) {
UIImageView *objImg = (UIImageView *)obj;
imageData = UIImagePNGRepresentation(objImg.image);;
break;
}
}
shindy.name = name;
shindy.dateAndTime = dateAndTime;
shindy.timePosted = postedTime;
shindy.details = details;
shindy.photo = imageData;
return shindy;
}
The method, - (void)setupFetchedResultsController: is called when the view is initially loaded (but not in the viewDidLoad or viewWillAppear method). And my - (void)saveNewShindyToDatabaseForName: function is called in a separate view controller once the user is finished entering their data. In other words, it's public API, but the NSManagedObjectContext remains within the HomeViewController for delegation. Also, the delegates & data sources for my code are set properly.
I have tried to force every kind of fetch; whether it be through the managedObjectContext, or the fetchedResultsController. I have also set an NSLog in various places of my code to ensure that the data is getting through to the HomeViewController. I have also set another NSLog to ensure that both the fetchedResultsController & managedObjectContext is not nil.
Everything seems to check out, and I don't get any sort of errors from the other default errors currently within my code.
I fear that this has to do something with the NSSortDescriptors I have in my - (void)setupFetchedResultsController: method, but I have also tried to configure those in various ways to no avail. There is a definite connection to my managedObjectContext & my fetchedResultsController as well, so that's not the problem.
I've simply run out of things to try. Perhaps, someone on here has some insight.
For jrturton...
I have implemented the following code in my - (void)saveNewShindyToDatabaseForName: method after all of the code that was there originally. It looks like this:
- (void)saveNewShindyToDatabaseForName:(NSString *)name
details:(NSString *)details
dateAndTime:(NSDate *)dateAndTime
location:(CLLocation *)location
timePosted:(NSDate *)postedTime
{
// All of the code originally in this method is here...
if ([self.fetchedResultsController managedObjectContext] == self.shindysDatabase.managedObjectContext) {
NSLog(#"MOC's are the same.");
}
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *detailsArray = [self.shindysDatabase.managedObjectContext executeFetchRequest:request error:&error];
if (error) {
NSLog(#"error: %#", error.userInfo);
}
NSLog(#"details: %#", detailsArray);
}
What all of this returns is that to answer your second possible answer, the MOC's are definitely the same. However, it appears as though the new objects are not actually getting saved into Core Data. Or I'm not pulling them back out properly.
The only way I could think of logging what is inside of my Core Data without getting an unrecognized selector error was by creating a new request for the correct entity, and then executing a fetch, then logging the results of that fetch, which, in this case, returned nothing.
I'm going to assume then that my problem lies within my code not saving the new objects to Core Data. With that said, can you identify anywhere I'm not properly saving these objects to my context so that it can be put into my Core Data model for retrieval later? Or do I need to setup a different method other than requesting to log what's being saved into my Core Data?
Possible issues:
No fetched results controller delegate (ok, resolved according to comments).
The FRC is potentially being set up before you have a database (you create a new database inside the first method, but say the FRC is set up before that - do they have the same MOC object?
Multithreading hell - your MOC save call will be executed before the block that calls the method that calls the method that passes the completion block (phew!). This could well result in the notifications not catching up.
You don't mention if the new objects ever enter the database - if you save, close and re-open the app, are they there? Are we dealing with a failure to send or receive change notifications, or a failure to make the changes?
I am working on an application which stores and retrieves data from sqlite db, using core data. For this I have created a separate class which acts like a data link layer - LocalDBController
Below is the implementation of one of its methods- selectAddressWithAddressId:
- (NSDictionary *)selectAddressWithAddressId:(NSString *)addressId
{
NSDictionary *dictToReturn = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"address_id == %#",addressId];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Address" inManagedObjectContext:self.moc]; // returning nil when invoked from test case class
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setPredicate:predicate];
NSError *err = nil;
NSArray *array = [self.moc executeFetchRequest:request error:&err];
// some more code...
return dictToReturn;
}
Now I am trying to implement a test case class for it (SenTestCase class).
I have written below init method in LocalDBController class, so that it uses the default persistent store if value of environment variable is 'Run' and uses in-memory persistent store if value of environment variable is 'Test':
- (id)init
{
if (self = [super init]) {
// initializing moc based on if run setting is used or test is used
if ([[[[NSProcessInfo processInfo] environment] objectForKey:#"TARGET"] isEqualToString:#"TEST"]) {
NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
[psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL];
self.moc = [[NSManagedObjectContext alloc] init];
self.moc.persistentStoreCoordinator = psc;
}
else
{
self.moc = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
}
return self;
}
In my test class I am trying to invoke below method:
STAssertNotNil([self.localDBController selectAddressWithAddressId:#"123"], #"No data found");
Problem is-
In this case, value of entityDescription obtained in selectAddressWithAddressId: method is nil, though the value of self.moc is not nil. So it
is throwing this exception msg in console: raised
executeFetchRequest:error: A fetch request must have an entity..
If I execute the above method from the class which is not included in my test case bundle, say appDelegate, it works fine.
Can anyone suggest me if I am doing anything wrong in it?
I have a set of items in a plist. When my app starts, I read in the plist and save it as an array in my DataManager singleton, like this:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *itemDatapath = [path stringByAppendingPathComponent:#"ItemData.plist"];
NSDictionary *itemData = [NSDictionary dictionaryWithContentsOfFile:itemDatapath];
dataManager.items = [itemData objectForKey:#"Items"];
I also want to store the core data objects that are associated with this data in the DataManger, so I attempted this:
-(void)setItems:(NSArray *)_items //causes EXC_BAD_ACCESS error
{
self.items = _items;
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *item in self.items)
{
NSManagedObject *itemObject = [NSEntityDescription
insertNewObjectForEntityForName:#"Item"
inManagedObjectContext:context];
[itemObject setValue:[NSNumber numberWithInteger:[[item valueForKey:#"id"] intValue]] forKey:#"identifier"];
[itemObject setValue:[UIImage imageNamed:[item valueForKey:#"image"]] forKey:#"image"];
...
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
The point being that anywhere in my app I can access the objects from this method:
-(NSArray*)fetchItems
{
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Item" inManagedObjectContext:managedObjectContext];
NSError *error2;
NSFetchRequest *itemFetchRequest = [[NSFetchRequest alloc] init];
[itemFetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"order"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[itemFetchRequest setSortDescriptors:sortDescriptors];
NSArray *fetchedItems = [managedObjectContext executeFetchRequest:itemFetchRequest error:&error2];
return fetchedItems;
}
The problem is the EXC_BAD_ACCESS error noted above. I would also like to know if there is a better way of going about this. I have the feeling storing the core data objects here is not the best practice. But even if I fetch the data when I need it in other view controllers, how can I manage updating the core data objects if they change? I have an external plist that may change, and the core data objects need to update based on that.
You are causing infinite recursion when you put self.items = _items inside the setItems: method. self.items is exactly the same as calling setItems - they invoke the same method. What you need to do instead is set the value of whatever your instance variable is - presumably items. So the first line of setItems: should be items = _items. That, in and of itself, is also confusing, as the convention is to have _ before variables indicate an instance variable.