I am working on a new project and for that I reuse my old saving and fetching code that is working just fine in the old app. Unfortunately, it doesn't work in my new project where I'm doing something very similar.
The user is presented with a table view and possibility to add new data by tapping the plus button that takes them to a different view controller where they can add the appropriate data and save. Saving works just fine without any problems.
Here's the code that is working fine:
+ (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).persistentContainer.viewContext;
return context;
}
In main view that is table view I try to fetch the data with the following code:
// Fetch the data from persistent data store
NSManagedObjectContext *context = [MGManagedObjectContext managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Data"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
self.dataArray = [[context executeRequest:fetchRequest error:nil] mutableCopy];
For whatever reason I get the "[NSAsynchronousFetchResult mutableCopyWithZone:]: unrecognized selector sent to instance 0x283619220" error that I can't work out why.
Any ideas how to fix this issue ?
Instead of method
- (__kindof NSPersistentStoreResult *)executeRequest:(NSPersistentStoreRequest *)request error:(NSError * _Nullable *)error;
use method
- (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError * _Nullable *)error;
Example:
self.dataArray = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
Related
I have been trying all afternoon to get the CoreData portion of my app working and almost have it working as it should. The problem is that the CoreData does not save appropriate value when the app gets terminated (removed from multitasking menu). The value that remains is the intitial value (in my case 0, defined as NSString) I put into the entity instead of the new value the user entered. So the issue is saving the new value PERMANENTLY after the app is closed and not showing the initial value again when the app is loaded
Entity name: Gameinfo
Attribute: score
I am not sure what I am doing wrong. If anyone could help me, it would be appreciated.
Thanks!
-(IBAction)saveTheory1
{
AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Gameinfo"];
NSError *error = nil;
NSArray *someArray = [context executeFetchRequest:request error:&error];
[[someArray objectAtIndex:0] setValue:[self showKey] forKey:#"score"]; // showKey gets the text from UITextField
}
-(IBAction)showTheory1;
{
AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"Gameinfo"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
matches = [objects objectAtIndex:0];
NSLog (#"show us the current value: %#", [matches valueForKey:#"score"]);
}
You are not saving the value at all. You are just assigning it to the Gameinfo object. To actually save it, call save: method on managed object context:
...
[[someArray objectAtIndex:0] setValue:[self showKey] forKey:#"score"];
NSError *saveError = nil;
[[appDelegate managedObjectContext] save:&saveError];
}
It doesnt look like you are calling [context save:error] anywhere. This is what moves your changes into permanent storage.
When my main view controller is loaded and it calls viewDidLoad I am performing a fetch request and retiring an array or my core data objects:
+ (NSArray *)getData {
// Fetch Data
NSError *error = nil;
if (![[[AppDelegate instance] fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSArray *items = [[AppDelegate instance].fetchedResultsController fetchedObjects];
return items;
}//end
For some reason, this returns an empty array when called from viewDidLoad.
If I call the same method from viewDidAppear: it works correctly and returns the NSArray of my CoreData objects.
Is there a reason why this won't work in viewDidLoad?
EDIT:
Fetched Results Controller method:
/**
* The controller the gets our results from core data
*
* #version $Revision: 0.1
*/
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SiteConfig" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array
NSSortDescriptor *sectionTitle = [[NSSortDescriptor alloc] initWithKey:#"createdDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sectionTitle, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
// Memory management
return fetchedResultsController;
}//end
My guess is that your view controller is part of your MainWindow.xib, and so its viewDidLoad is being called before your app delegate gets the core data context ready.
You probably want to run this in viewWillAppear anyway, so that you'll get new data if you leave the screen and return. The other option would be to ensure the app delegate responds to fetchedResultsController by getting the core data stack ready if it isn't already.
I have a helper class designed to get data from Core Data and pass it back to a view controller. Here's the code:
View Controller:
#interface AnnouncementsController : UIViewController
#property (nonatomic, retain) NSMutableArray *announcements;
#end
#implementation AnnouncementsController
#synthesize announcements;
- (void)viewDidLoad
{
AnnouncementCoreDataHelper *announcementHelper = [[AnnouncementCoreDataHelper alloc] init];
self.announcements = [announcementHelper selectAnnouncementsWithPredicate:#"isActive = 1" sortDescriptor:#"lastUpdated" sortAscending:NO];
[super viewDidLoad];
}
#end
And the Core Data Helper implementation:
// Select one or more announcements based on a predicate if passed in and a sort order
- (NSMutableArray*)selectAnnouncementsWithPredicate:(NSString *)predicateString sortDescriptor:(NSString *)sortBy sortAscending:(BOOL)isAscending
{
NSError *error = nil;
// Build the entity and request
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Announcement" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
if(predicateString)
{
// Set the search criteria
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
[request setPredicate:predicate];
}
if(sortBy)
{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortBy ascending:isAscending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Perform the search
NSMutableArray *results = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if(results == Nil)
{
NSLog(#"%#", error);
}
return results;
}
The problem is that the Core Data Helper returns two objects in its NSMutableArray results but the View Controller's NSMutableArray announcements gets nil. What is getting lost in the passing the NSMutableArray back from the helper to the controller? If I change all the variables and results to a NSArray, everything works.
I believe this was a funky issue with Xcode. I clean and reran the build and everything is working. I apologize for wasting your time and thanks for the help! I would like to know more about the memory management issues though.
I am trying to read an array from Core Data. My code is as follows
-(NSArray *)getAges
{
NSArray *fetchedObjects = [[NSArray alloc] init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = nil;
context = [[NSManagedObjectContext alloc] init];
#try {
NSPersistentStoreCoordinator *tempCordinator = (NSPersistentStoreCoordinator *)[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
[context setPersistentStoreCoordinator:tempCordinator];
NSError *error;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Age"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
}
#catch (NSException *exception) {
NSLog(#"core data exception occured.");
fetchedObjects = nil;
}
[fetchRequest release];
[context release];
return fetchedObjects;
}
Now in main thread, in an IBAction method, I write following line.
[NSThread detachNewThreadSelector:#selector(loadAgesMethod) toTarget:self withObject:nil];
Problem is that this method returns me an array of 20 elements. I can see the array count 20 but in debugger, against every element of array, i see 'out of scope' and 'summary unavailable'. And when I try to fetch any object from this array, i get nothing. One thing is to mention is that I am calling this method getAges in loadAgesMethod from a secondary thread as I mentioned above. This is my requirement. Also when I try to retrieve this array from main thread, i get all data perfectly ok.
Can anyone please give me a hint, where I am wrong?
Best regards
Every NSManagedObject has a NSManagedObjectContext property. This is used to lazily fetch properties of the object when required in the future. At the end of this method you call [context release] hence the NSManagedObjectContext will be deallocated and all your NSManagedObject context properties will be nil for your Age objects.
You could call [context autorelease] on the context so that it persists for some time after the method and allows you to fetch properties on the objects at a later stage. Note this will only persist until the autorelease pool drains at the end of the run loop. I am still trying myself to come up with a better solution to multithreaded fetching.
The is the code for the fetchRequest in viewDidLoad and the code is followed from a tutorial found here just that I'm linking the navigation controller and the tableview programmatically instead of using interface builder. The entity ProductInfo exists. However when I run the program I get the error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'ProductInfo''
I have reset the simulator incase it was an old model but the error still occurs. I have also switched to use a FetchedResultsController but the problem still persists. Is the problem because these fetchedResultsController methods aren't inside the appdelegate? They are currently in a TableViewController. How can I solve this problem?
viewDidLoad Method:
- (void)viewDidLoad{
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"ProductInfo" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError * error;
self.productInfos = [_context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
[super viewDidLoad];}
ProductInfo.h:
#class ProductDetails;
#interface ProductInfo : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * productName;
#property (nonatomic, retain) NSString * productPrice;
#property (nonatomic, retain) ProductDetails * details;
#end
FetchedResultsController
-(NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"ProductInfo" inManagedObjectContext:_context]; //line that is causing the problem
[fetchRequest setEntity:entity];
NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:#"productInfos.productName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController * theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext: _context sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = nil;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return _fetchedResultsController;
}
Any help much appreciated.Thanks in advance.
In case the above fragments i pasted did not help, I attached the whole project with the data model inside too.
http://www.mediafire.com/?5cns4q0sv9hqn6s
This happens to me every time a switch development computers. What I have to do is
Uninstall the app from the simulator.
Rename the database file that stores the UIManagedDocument.
Perform a Build Clean.
Build and run the application.
Having run into this problem before, it sounds like it might be a simple misspelling of the entity name in the xcdatamodeld file. The string "ProductInfo" must match exactly the name of the entity in the model file.
It might also be that your context is not making the correct reference. Consider showing some more of your code related to the context if the above doesn't fix the issue.