0

I'm trying to save data into core data after the completion of a web request. In AppDelegate I have my context. In the following the code for get the context:

- (NSManagedObjectContext *)contex {
    @synchronized(self) {
        if (_contex == nil) {
            _contex = [[NSManagedObjectContext alloc] init];
            [_contex setPersistentStoreCoordinator:self.persistentStoreCoordinator];
        }
        return _contex;
    }
}

Like the Apple guidelines say, I have one persistent store coordinata shared with multiple contexts.

This is the code where I take the data from web, get the context and make the call to the method for save the new value into core data.

NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error) {
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
            UIImage *image;
            if (!error) {
                NSData *imageData = [NSData dataWithContentsOfURL:localFile];
                image = [UIImage imageWithData:imageData];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"thumbnailIsSetted" object:self userInfo:@{@"image": image,
                                                                                                                           @"PhotoUrl": photo.url}];
                    @synchronized(self) {
                        NSManagedObjectContext *context = [(SocialMapAppDelegate *)[[UIApplication sharedApplication] delegate] contex];
                        [Photo addThumbnailData:imageData toPicture:photo.url fromContext:context];
                    }
                });
            } else {
                //if we get an error, we send the default icon
                image = [UIImage imageNamed:@"Photo-thumbnail"];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"thumbnailIsSetted" object:self userInfo:@{@"cached image": image,
                                                                                                                           @"PhotoUrl": photo.url}];
                });
            }
        }];
        [task resume]; //we resume the task in case it is souspended
    }];

and this is the method that I use for save the data into core data:

+ (void)addThumbnailData:(NSData *)data toPicture:(NSString *)pictureUrl fromContext:(NSManagedObjectContext *)context 
{
    if (pictureUrl && context) {
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
        request.predicate = [NSPredicate predicateWithFormat:@"url == %@",pictureUrl];
        NSError *error = nil;
        NSArray *matches = [context executeFetchRequest:request error:&error];
        if ([matches count] == 1) {
            Photo *photo = [matches lastObject];
            photo.thumbnailData = data;

            if (![context save:&error]) {
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            }
        }
    }
}

I can't figure where is the problem, the context is not nil, and I don't get any error. If i restart the app the data are there.

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
Max_Power89
  • 1,710
  • 1
  • 21
  • 38

3 Answers3

1

I do not recommend using multiple contexts unless you are using a multi-threaded design in your application. I would recommend stripping the app down to one context to avoid having to pass notifications around. It simply isn't needed 99% of the time.

In addition, if you are using parent/child contexts, I do not recommend re-using the child contexts. They should be transient; used for a single task and then thrown away. The reason for this is that data changes only flow one way; up. If you change something in one child it does not get pushed to any siblings. Further, child contexts go stale. If you make a change in the main context, all of the children will be out of date.

Your UI should be using a single NSManagedObjectContext across the entire application. That will most likely resolve your issues.

Marcus S. Zarra
  • 46,571
  • 9
  • 101
  • 182
  • but i read in this thread, [link](http://stackoverflow.com/questions/6622699/singleton-managedobjectcontext) that have one single NSManagedObjectContext across the entire application is a bad practice. – Max_Power89 Dec 23 '13 at 18:31
  • that link says something very different. it discourages the vcs getting context themself. they should be passed a context => increase reusability – Daij-Djan Dec 23 '13 at 20:07
  • There is a *huge* difference between having a single context and using a Singleton. Using a Singleton is bad because you cannot destroy it or reset easily. Using a single `NSManagedObjectContext` for the `UI` is the recommended approach. – Marcus S. Zarra Dec 24 '13 at 03:51
0

the code looks ok... the save persists the context's content to disk alright. to refresh other contexts you have to listen to the ContextDidSave notification and merge in the data

first observe:

...
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(mergeChanges:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:managedObjectContext];            
...

then merge

- (void)mergeChanges:(NSNotification *)notification {
    // Merge changes into the main context on the main thread
    [self.mainManagedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
    withObject:notification
 waitUntilDone:YES];
}
Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • you call mergeChangesFromContextDidSaveNotification on the right context? and it isn't merged in? .. also of course you have to refresh your UI after the merge is done – Daij-Djan Dec 22 '13 at 23:39
  • yes the context is right, and the merger is executed, but when i update the view, and send again the request to core data,this doesn't have any effect, and the data are download again from internet – Max_Power89 Dec 23 '13 at 19:27
0

i solved, the problem was on the class where i visualize the and ask for the data, in that class instead of get the context from the appDelegate on this way:

[(SocialMapAppDelegate *)[[UIApplication sharedApplication] delegate] contex];

i did this

[[SocialMapAppDelegate alloc] init].contex

in this way i simple create another context and another store, thing that is totally wrong, so i solved simply by substitute the code above with

    [(SocialMapAppDelegate *)[[UIApplication sharedApplication] delegate] contex];
Max_Power89
  • 1,710
  • 1
  • 21
  • 38