I am using CoreData in my app and I have two main entities, let's say Cats and Dogs.
I have this method that I use to initialise my context:
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
It refers to this method in my App Delegate:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Now, at some point, I have to update some data in my Cats entity.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Cats"];
NSMutableArray *catsArrayData = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSManagedObject *aCat = [catsArrayData objectAtIndex:i];
NSMutableArray *catsArray = [aCat valueForKey:#"cat"];
[catsArray addObject:responseData];
[aCat setValue:catsArray forKey:#"cat"];
NSError *error = nil;
[context save:&error];
Then, I want to do the same for my Dogs entity. So I use the same code, but for the dogs entity. It's in another part of my code, so I have to redefine:
NSManagedObjectContext *context = [self managedObjectContext];
But the problem is that even though I save this second context, it is actually not saved. The data isn't there. How can I resolve this issue?
You need implement your own change notification mechanism, This is good source to learn it. Multi-Context CoreData.
Related
When I start app and enter some data to Core Data via UITextField, they are displayed correctly in UITableView. Problem is when I restart the app, all data from Core Data is lost... Here is my code.
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
_managedObjectContext = [appDelegate managedObjectContext];
_managedObjectModel = [appDelegate managedObjectModel];
_persistentStoreCoordinator = [appDelegate persistentStoreCoordinator];
}
- (IBAction)saveButtonPressed:(id)sender {
Truck *truck = [NSEntityDescription insertNewObjectForEntityForName:#"Truck" inManagedObjectContext:_managedObjectContext];
[truck setModel:self.tField.text];
}
Once you create your truck object, you must save the context so that it gets written to the database.
- (IBAction)saveButtonPressed:(id)sender {
Truck *truck = [NSEntityDescription insertNewObjectForEntityForName:#"Truck" inManagedObjectContext:_managedObjectContext];
[truck setModel:self.tField.text];
[_managedObjectContext save:nil]; // you should provide an NSError object
}
I'm getting the error "Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Context already has a coordinator; cannot replace.'" while executing my app.
In the DataBaseManager.m I have the following code :
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *privateWriterContext = [sharedDelegate privateWriterContext];
NSManagedObjectContext *context = [sharedDelegate managedObjectContextChild];
context.parentContext = privateWriterContext;
NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
contextforThread.parentContext = context;
[contextforThread performBlock:^{
//things to do in background
NSError *error;
if (![contextforThread save:&error])
{
// handle error
}
[context performBlock:^{
NSError *error;
if (![context save:&error])
{
// handle error
}
[privateWriterContext performBlock:^{
NSError *error;
if (![privateWriterContext save:&error])
{
// handle error
}
}];
}];
}];
and in appDelegate.m I have the following code :
- (NSManagedObjectContext *)managedObjectContextChild
{
if (_managedObjectContextChild != nil) {
return _managedObjectContextChild;
}
_managedObjectContextChild = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
return _managedObjectContextChild;
}
- (NSManagedObjectContext *)privateWriterContext
{
if (_privateWriterContext != nil) {
return _privateWriterContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateWriterContext setPersistentStoreCoordinator:coordinator];
}
return _privateWriterContext;
}
Xcode shows the line
context.parentContext = privateWriterContext;
when error occurs.
How could I rectify this error?
At the top of - (NSManagedObjectContext *)managedObjectContextChild put a breakpoint and see where else that NSManagedObjectContext is being used.
The error is telling you that it already has a persistent store coordinator, so that's your line of attack.
You are accessing these as single instances so the other areas of the application need to know what to do with them (and what not to do!). You could also inspect persistentStoreCoordinator and parentContext property just before the error to help get a picture of what's happening.
It's nothing terrible and shouldn't be too difficult to find with a couple of sensibly placed breakpoints, however you may have to think about how you manage the various contexts more carefully to solve the issue.
When in Xcode 5 I am using the code below, which is in the #interface area, to get data from my CoreData model. It is telling me that I should insert a semi-colon here,
- (NSManagedObjectContext *)managedObjectContext;
Any help is appreciated!
Below is my full code block.
//Get Data
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
When in Xcode 5 I am using the code below, which is in the #interface area, to get data from my CoreData model.
This is you problem, you're only supposed to but declarations in the #interface. Actual implementations go in the #implementation section.
Your interface goes
#interface
- (NSManagedObjectContext *)managedObjectContext;
#end
In you implementation you put the actual method:
#implementation
...
//Get Data
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate respondsToSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
...
#end
UPDATE:
Thanks to Abizern for noticing that there was another bug in your code: the line [delegate performSelector:#selector(managedObjectContext)] should actually be [delegate respondsToSelector:#selector(managedObjectContext)], because otherwise the application will crash if it doesn't respond to the selector.
you have weird getter in my opinion, try ti use something like this one
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil)
{
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
In my application, while executing fetch request application get freeze randomly. I have tried with multiple Choice like #synchronized and performblock still hang occur. below is my first fetch request block. Application hang in this fetch request randomly.
+(BXXXX *)getDetailsById:(NSNumber *)Id
{
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"BXXXX" inManagedObjectContext:[SDataManager managedObjectContext]];
[fetch setEntity:entityDescription];
[fetch setPredicate:[NSPredicate predicateWithFormat:
#"(BId = %#)",Id]];
__block NSArray *bDetails;
[[SDataManager managedObjectContext] performBlockAndWait:^{
NSError *error = nil;
bDetails = [[SDataManager managedObjectContext] executeFetchRequest:fetch error:&error];
}];
if([bDetails count] == 1)
return [bDetails objectAtIndex:0];
else
return nil;
}
//MY Managed object context declaration
+(NSManagedObjectContext *)managedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if(managedObjectContext!=nil){
return managedObjectContext;
}
#try {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
}
Please guide me to fix this issue. I tried hard but i cant fix this issue still now.
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
Your managed object context is going to do all work on the main queue. This will block the main event loop and cause your app to appear to hang.
Move the work off the main queue. See the Core Data Concurrency Guide.
I have fixed this issue by creating two managed object context like this.
// Base
+(NSManagedObjectContext *)managedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if(managedObjectContext!=nil){
return managedObjectContext;
}
#try {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
}
//Child
+(NSManagedObjectContext *)childManagedObjectContext
{
static NSManagedObjectContext *managedObjectContext;
if(managedObjectContext!=nil){
return managedObjectContext;
}
#try {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
}
for executing the fetch request i have used child managed object context with Concurrency Type as NSPrivateQueueConcurrencyType. It working fine for me. Now UI hang is not there.
Our table view controllers use an NSFetchedResultsController to show data from Core Data. We download new data in the background. When an entity is modified in the new data, on iOS 5.1.1 phone, we see that treated as a new row in the table instead of an update. Cannot duplicate on the iOS 5.1 simulator or an iOS 6 device.
The UIApplicationDelegate creates a NSManagedObjectContext with concurrency type NSMainQueueConcurrencyType. Our UITableViewController implements NSFetchedResultsControllerDelegate. In viewWillAppear we go fetch new data. In the method getting data, we create a second NSManagedObjectContext with concurrenty Type NSPrivateQueueConcurrencyType. We do a performBlock on that new context, and do the network call and json parsing. There's a NSFetchRequest to get the previous data, so we can delete the old objects, or modify any existing entities with the same id. After modify the existing entity or creating new ones, we then deleteObject the old entity objects. Then we save this private context. Then on the parent context, do a performBlock to save the changes there.
On iOS5.1, the table is incorrect. If we change on of the objects, instead of being modified, it is added to the table as a new row. If we leave this controller and come back to it, getting new data, it shows the right amount.
AppDelegate.m
- (void)saveContext
{
[self.privateWriterContext performBlock:^{
NSError *error = nil;
[self.privateWriterContext save:&error];
// Handle error...
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.privateWriterContext];
}];
}
#pragma mark - Core Data stack
- (NSManagedObjectContext *)privateWriterContext
{
if (__privateWriterContext != nil) {
return __privateWriterContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[__privateWriterContext setPersistentStoreCoordinator:coordinator];
}
return __privateWriterContext;
}
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[__managedObjectContext setParentContext:self.privateWriterContext];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(saveContext:)
name:NSManagedObjectContextDidSaveNotification
object:__managedObjectContext];
return __managedObjectContext;
}
class that fetches from server
+ (void) fetchFromURL:(NSString *) notificationsUrl withManagedObjectContext (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
importContext.parentContext = managedObjectContext;
[importContext performBlock: ^{
NSError *error;
NSURLResponse *response;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *responseData = [NSData dataWithContentsOfURLUsingCurrentUser:[NSURL URLWithString:notificationsUrl] returningResponse:&response error:&error];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSMutableSet *newKeys = [[NSMutableSet alloc] init];
NSArray *notifications;
if(responseData) {
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSMutableDictionary *previousNotifications = [[NSMutableDictionary alloc] init];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Notification"];
NSArray * oldObjects = [importContext executeFetchRequest:request error:&error];
for (Notification* oldObject in oldObjects) {
[previousNotifications setObject:oldObject forKey:oldObject.notificationId];
}
notifications = [json objectForKey:#"notifications"];
//create/update objects
for(NSDictionary *notificationDictionary in notifications) {
NSString *notificationId = [notificationDictionary objectForKey:#"id"];
Notification *notification = [previousNotifications objectForKey:notificationId];
if(notification) {
[previousNotifications removeObjectForKey:notificationId];
} else {
notification = [NSEntityDescription insertNewObjectForEntityForName:#"Notification" inManagedObjectContext:importContext];
[newKeys addObject:notificationId];
}
notification.notificationId = [notificationDictionary objectForKey:#"id"];
//other properties from the json response
}
for (NSManagedObject * oldObject in [previousNotifications allValues]) {
[importContext deleteObject:oldObject];
}
}
if (![importContext save:&error]) {
NSLog(#"Could not save to main context after update to notifications: %#", [error userInfo]);
}
//persist to store and update fetched result controllers
[importContext.parentContext performBlock:^{
NSError *parentError = nil;
if(![importContext.parentContext save:&parentError]) {
NSLog(#"Could not save to store after update to notifications: %#", [error userInfo]);
}
}];
}
];
}
I suffered the problem recently too.
The problem is because of two contexts in different threads.
On device which is running iOS 5.1, merging them cause it to insert a new record instead of update it. I change the background thread to use main context instead and the problem is gone.
No clue why merging does not work in this particular case.