NSFetchedResultsController multiple entities for UITableView - ios

I have two entities one called Post and one called User. Post<<---->User is the relationship in core data. I am using a NSFetchedResultsController to fetch all Post records in my core data stack and then displaying them in a UITableView. Each cell has an image and that image corresponds to a User.profilePicture.
Upon initializing I do not download the profile picture from the server, I only download when it scrolls past that cell (lazy load). Once I download it I save the downloaded image to the corresponding User.profilePicture in the core data stack.
Is there a way for controllerDidChangeContent to be called when I update the User entity?? My current understanding is that my NSFetchedResultsController can only follow the Post entity since that is what I initially set it to do and cannot traverse and monitor updates across a relationship, is that true?

Sadly I know only of an UGLY solution for this issue.
In your User .m file implements the setProfilePicture: like this:
//NOT TESTED IN A MULTITHREADED ENV
- (void) setProfilePicture:(NSData *)data
{
[self willChangeValueForKey:#"profilePicture"];
[self setPrimitiveValue:data forKey:#"profilePicture"];
[self.posts enumerateObjectsUsingBlock:^(Post* p, BOOL *stop) {
[p willChangeValueForKey:#"user"];
[p didChangeValueForKey:#"user"];
}];
[self didChangeValueForKey:#"profilePicture"];
}
This will notify the FRC that the Post element has changes.
You might find additional information here
Edit:
To fetch the data on access you can add this to your User.m:
//UNTESTED
+ (void) mergeToMain:(NSNotification*)notification
{
AppDelegate* appDel = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[appDel.managedObjectContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
- (NSData*)_profilePicture
{
return [self primitiveValueForKey:#"profilePicture"];
}
- (NSData*) profilePicture
{
[self willAccessValueForKey:#"profilePicture"];
NSData* picData = [self primitiveValueForKey:#"profilePicture"];
if (!name) {
__block NSManagedObjectID* objectID = self.objectID;
//This solves the multiple downloads per item by using a single queue
//for all profile pictures download.
//There are more concurrent ways to accomplish that
dispatch_async(downloadSerialQueue, ^{ //define some serial queue for assuring you down download multiple times the same object
NSError* error = nil;
AppDelegate* appDel = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext* context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:appDel.persistentStoreCoordinator];
[context setUndoManager:nil];
User* user = (User*)[context existingObjectWithID:objectID error:&error];
if (user && [user _profilePicture] == nil) {
NSData *data = //[method to retrieve data from server];
if (data) {
if (user) {
user.profilePicture = data;
} else {
NSLog(#"ERROR:: error fetching user: %#",error);
return;
}
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:#selector(mergeToMain:) name:NSManagedObjectContextDidSaveNotification object:context];
[context save:&error];
[[NSNotificationCenter defaultCenter] removeObserver:[self class] name:NSManagedObjectContextDidSaveNotification object:context];
}
}
});
}
[self didAccessValueForKey:#"profilePicture"];
return picData;
}

I think this issue can be solved without NSFetchedResultsController involved.
use SDWebImage, SDWebImage can load images from remote server asynchronously, just do this:
[myImageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
use KVO, add a observer to User entity and update corresponding image view accordingly. But the code for KVO is rather complex, ReactiveCocoa can simplify them:
[RACAble(user.profilePicture) subscribeNext:^(UIImage *image) {
[myImageView setImage:image];
}];

Related

Saving context concurrently in Core Data not working in iOS7

I get some data from a couple of web services that are called asynchronously. When I receive their responses, I need to create and save corresponding entities in Core Data with the information received. Since the services callbacks ara asynchronous, and I could be already saving the response of one of the services when I receive the another, I wrote a couple of methods like this:
- (void)createEntity
{
#autoreleasepool {
dispatch_queue_t queue = dispatch_queue_create(kSaveQueue, NULL);
dispatch_async(queue, ^{
// Context for background operations
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
NSPersistentStoreCoordinator *mainThreadContextPSC = [self.context persistentStoreCoordinator];
[tmpContext setPersistentStoreCoordinator:mainThreadContextPSC];
#try {
// Parse service response and create entity
// Save context
[tmpContext save:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// Notify end of operation
});
}
#catch (NSException *ex) {
NSLog(#"exception: %#", [ex description]);
}
});
}
}
Actually, I have two methods like this, one for let's say EntityA, and another for EntityB, and each one is called when I receive the corresponding service response (serviceA, serviceB). In my tests I see that both tmpContext are always saved in iOS 8, but in iOS 7 it is only the first called which is saved, and the second entity is not persisted in Core Data.
Why does this work in iOS 8 but it doesn't in iOS 7?
Thanks in advance
Your approach to create context with alloc init and then assign the persistent store coordinator is deprecated.
Instead, use the factory method initWithConcurrencyType: and pass NSPrivateQueueConcurrencyType for a background thread. Associate with the parent context by calling setParentContext:.
You can also do background operations by taking advantage of the context's performBlock and performBlockAndWait APIs rather than dropping down to GCD.
Above answer from Mundi is right and good explanations.. I can give you the code I use to create a thread context and save and stop context
+ (NSManagedObjectContext*)startThreadContext {
AppDelegate *theDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = theDelegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
// get thread dictionary
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
if ( [threadDictionary objectForKey:#"managedObjectContext"] == nil ) {
// create a context for this thread
NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[newMoc setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]];
// Register for context save changes notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:newMoc];
[newMoc setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[newMoc processPendingChanges]; // flush operations for which you want undos
[[newMoc undoManager] disableUndoRegistration];
newMoc.undoManager = nil;
// cache the context for this thread
[threadDictionary setObject:newMoc forKey:#"managedObjectContext"];
}
return [threadDictionary objectForKey:#"managedObjectContext"];
}
+ (void)saveAndStopThreadContext:(NSManagedObjectContext *)context {
// save managed object
NSError* error = nil;
BOOL success = [context save:&error];
if ( !success ) {
ERRLOG(#"[stopThreadContext] failed to save managedObjectContext (err:%#)", error );
}
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:context];
NSThread *thread = [NSThread currentThread];
if (![thread isMainThread]) {
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
[threadDictionary removeObjectForKey:#"managedObjectContext"];
}
}
And you can use it like this
// get managed object context
NSManagedObjectContext* moc = [CoreDataHelper startThreadContext];
// perform update
[moc performBlock:^{
/*
Do something...
*/
// save and stop thread context
[CoreDataHelper saveAndStopThreadContext:moc];
}];

CoreData nested contexts: what is the proper way to save context?

I am using nested contexts pattern to support multithreaded work with CoreData.
I have CoredDataManager singleton class and the inits of contexts are:
self.masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.masterContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainContext.parentContext = self.masterContext;
For each insert operation on response from web service I use API of my CoreDataManager to get new managed context:
- (NSManagedObjectContext *)newManagedObjectContext {
NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerContext.parentContext = self.mainContext;
return workerContext;
}
It looks something like (PlayerView class is subclass of NSManagedObject class):
[PlayerView insertIfNeededByUniqueKey:#"playerViewId" value:playerViewId inBackgroundWithCompletionBlock:^(NSManagedObjectContext *context, PlayerView *playerView) {
playerView.playerViewId = playerViewId;
playerView.username = playerViewDictionary[#"name"];
[context saveContextWithCompletionBlock:^{
//do something
} onMainThread:NO];//block invocation on background thread
}];
saveContextWithCompletionBlock method is implemented in NSManagedObjectContext category:
- (void)saveContextWithCompletionBlock:(SaveContextBlock)completionBlock onMainThread:(BOOL)onMainThread {
__block NSError *error = nil;
if (self.hasChanges) {
[self performBlock:^{
[self save:&error];
if (error) {
#throw [NSException exceptionWithName:NSUndefinedKeyException
reason:[NSString stringWithFormat:#"Context saving error: %#\n%#\n%#", error.domain, error.description, error.userInfo]
userInfo:error.userInfo];
}
if (completionBlock) {
if (onMainThread && [NSThread isMainThread]) {
completionBlock();
} else if (onMainThread) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
} else if ([NSThread isMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
completionBlock();
});
} else {
completionBlock();
}
}
}];
}
}
Then on some stage I'm calling method of CoreDataManager to save master context:
- (void)saveMasterContext; {
__block NSError *error;
[self.mainContext performBlock:^{
[self.mainContext save:&error];
[self treatError:error];
[self.masterContext performBlock:^{
[self.masterContext save:&error];
[self treatError:error];
}];
}];
}
I have two main classes, subclasses of NSManagedObject - PlayerView and Post.
PlayerView has relation one to many to Post.
PlayerView is saved and is ok. The Post is never saved and I get error:
CoreData: error: Mutating a managed object 0x17dadd80 (0x17daf930) after it has been removed from its context.
I think, that the problem is in contexts saving logic.
First of all, the error you're experiencing usually happens when the context in which you created the new managed object goes away (released) before you had the chance to save it.
Secondly, the best way to make sure the context is saved in the appropriate thread is to use performBlock or performBlockAndWait instead of trying to figure out which thread the context belongs to. Here's a sample "save" function that saves the context safely:
+ (BOOL)save:(NSManagedObjectContext *)context {
__block BOOL saved = NO;
[context performBlockAndWait: {
NSError *error;
saved = [context save:&error];
if (!saved) {
NSLog("failed to save: %#", error);
}
}]
return saved;
}
As for using nested private contexts (with main thread context as the parent), our team experienced some issues with that model (can't recall exactly what it was), but we decided to listen for NSManagedObjectContextDidSaveNotification and use mergeChangesFromContextDidSaveNotification to update contexts.
I hope this helps.
A great tutorial by Bart Jacobs entitled: Core Data from Scratch: Concurrency describes two approaches in detail, the more elegant solution involves parent/child managed object contexts, including how to properly save context.

CoreData child contexts, NSFetchedResultsController and main thread

Following this excellent post by Olivier Drobnik, I've implemented the three-layer CoreData stack proposed by CoreData guru Marcus S. Zarra:
The only difference from this diagram and my code is that I only use one Temporary Background MOC, in order to avoid duplicates when inserting objects in several temp MOCs. Here's my context initialisation code:
#pragma mark - NSManagedObjectContexts
+ (NSManagedObjectContext *)privateManagedObjectContext
{
if (!_privateManagedObjectContext) {
// Setup MOC attached to PSC
_privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateManagedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
// Add notification to perform save when the child is updated
_privateContextSaveObserver =
[[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
NSManagedObjectContext *savedContext = [note object];
if (savedContext.parentContext == _privateManagedObjectContext) {
[_privateManagedObjectContext performBlock:^{
NSLog(#"AMBCoreData -> saving privateMOC");
NSError *error;
if (![_privateManagedObjectContext save:&error]) {
NSLog(#"AMBCoreData -> error saving _privateMOC: %# %#", [error localizedDescription], [error userInfo]);
}
}];
}
}];
}
return _privateManagedObjectContext;
}
+ (NSManagedObjectContext *)mainUIManagedObjectContext
{
if (!_mainUIManagedObjectContext) {
// Setup MOC attached to parent privateMOC in main queue
_mainUIManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainUIManagedObjectContext setParentContext:[self privateManagedObjectContext]];
// Add notification to perform save when the child is updated
_mainUIContextSaveObserver =
[[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
NSManagedObjectContext *savedContext = [note object];
if (savedContext.parentContext == _mainUIManagedObjectContext) {
NSLog(#"AMBCoreData -> saving mainUIMOC");
[_mainUIManagedObjectContext performBlock:^{
NSError *error;
if (![_mainUIManagedObjectContext save:&error]) {
NSLog(#"AMBCoreData -> error saving mainUIMOC: %# %#", [error localizedDescription], [error userInfo]);
}
}];
}
}];
}
return _mainUIManagedObjectContext;
}
+ (NSManagedObjectContext *)importManagedObjectContext
{
if (!_importManagedObjectContext) {
// Setup MOC attached to parent mainUIMOC in private queue
_importManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_importManagedObjectContext setParentContext:[self mainUIManagedObjectContext]];
}
return _importManagedObjectContext;
}
This code is pretty straightforward. I'm replicating the above diagram using only the mainUIManagedObjectContext in the NSMainQueueConcurrencyType. Every time the child context, importManagedObjectContext gets saved, a notification is fired and all the parent contexts performs a save in it's current thread.
I've implemented a test view controller with a UITableView and a NSFetchedResultsController attached. This is the code in the viewDidLoad of my test view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Task"];
[request setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"insertDate" ascending:NO]]];
self.fetchRequest = request;
NSFetchedResultsController *frc =
[[NSFetchedResultsController alloc]
initWithFetchRequest:self.fetchRequest
managedObjectContext:[AMBCoreData mainUIManagedObjectContext]
sectionNameKeyPath:nil
cacheName:nil];
frc.delegate = self;
[self setFetchedResultsController:frc];
[self.fetchedResultsController performFetch:nil];
}
Here I attach the mainUIManagedObjectContext to the NSFetchedResultsController. Later, in my viewDidAppear, I run a loop to insert a few Task entities:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[AMBCoreData importManagedObjectContext] performBlock:^{
for (int i = 0; i < 5000; i++) {
Task *task = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:[AMBCoreData importManagedObjectContext]];
task.title = [NSString stringWithFormat:#"Task %d", i];
task.insertDate = [NSDate new];
[[AMBCoreData importManagedObjectContext] save:nil];
}];
}
The thing is, I'm inserting 5000 objects and the UI is freezing when the data is populated into the table view. Florian Kugler ran a test with this architecture inserting 15.000 objects and with instruments he got this main thread usage (blue is for main thread, gray for any other threads):
But here's my main thread CPU usage with 5000 objects, profiled using an iPhone 5:
As you can see, my main thread usage is far greater than Florian's and also my UI freezes for a few seconds. My question is, am I doing something wrong? Is this the expected behaviour when using this three-layer MOC architecture with a NSFetchedResultsController and a UITableView? I know that inserting 5000 objects is not the usual behaviour of most apps, so when I've tried with 50 or 100 objects the freeze was inexistent or unnoticeable, but the main thread usage was high (although I admit that in this case it can be due another reasons like waking up the app).
Yes, it is expected, because Main MOC is involved in the saves of its children. It is convenient and kind of okay when children of the UI context don’t do big saves, but often becomes a performance problem if those saves are bigger. You can’t be sure that the UI thread does only minimum job when using this pattern.
For the big saves I would recommend creating a context that is configured directly with the persistent store coordinator. After big save happens, you just refetch and optionally refresh data in the UI context. For more details see my answer here.

Core Data doesn't save data until the app is closed

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.
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.
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];
}
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];

Accessing the objectID of a NSManagedObject on a different thread?

Part of my UITableViewCell's content creation is delayed by the fault that happens on one object's (CoreData NSManagedObject) initial access. This manifests itself in a small hiccup the cell is first scrolled into view. I decided to push that access of those objects off to a background thread.
This is how I implemented it and it works well, but we all know that we are not supposed to access one thread(the main thread)'s NSManagedObjectContext in another thread, but can we get the objectID of an object in a second thread if it was originally fetched in the first thread?
Getting the objectID takes a small amount of time, which I was hoping to push into the background with everything else.
MyRecord *record = [self.frc objectAtIndexPath: indexPath];
// Should the following be here or can it be below in the background thread?
// NSManagedObjectID *recordObjectID = record.objectID;
dispatch_async(_recordViewQueue, ^(void) {
if ([cell.origIndexPath isEqual:indexPath]) {
// should the following be here or above? It works here, but am I just lucky?
// this call seems to take about 2/100 of a second
NSManagedObjectID *recordObjectID = record.objectID;
NSManagedObjectContext *bgndContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
bgndContext.persistentStoreCoordinator = App.sharedApp.storeCoordinator;
MyRecord *newRecord = (MyRecord *) [bgndContext objectWithID:recordObjectID];
[self updateCell:cell withRecord:newRecord];
if ([cell.origIndexPath isEqual:indexPath]) {
dispatch_async(dispatch_get_main_queue(), ^{
[(UIView*) cell.recordView setNeedsDisplay];
});
}
}
});
Is this safe? Or do I have to get the objectID in the mainThread?
It is safe to pass the objectID of a managed object between threads. It is not safe to use a managed object between threads. Use the objectID and your thread's managed object context to call existingObjectWithID:error: to get an instance of the managed object for that thread.
I would update your code like so:
MyRecord *record = [self.frc objectAtIndexPath: indexPath];
NSManagedObjectID *recordObjectID = record.objectID;
dispatch_async(_recordViewQueue, ^(void) {
if ([cell.origIndexPath isEqual:indexPath]) {
NSManagedObjectContext *bgndContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
bgndContext.persistentStoreCoordinator = App.sharedApp.storeCoordinator;
NSError * error = nil;
MyRecord *newRecord = (MyRecord *) [bgndContext existingObjectWithID:recordObjectID error:&error];
if (newRecord) {
[self updateCell:cell withRecord:newRecord];
if ([cell.origIndexPath isEqual:indexPath]) {
dispatch_async(dispatch_get_main_queue(), ^{
[(UIView*) cell.recordView setNeedsDisplay];
});
}
}
else {
NSLog(#"unable to find existing object! error: %# (userInfo: %#)", [error localizedDescription], [error userInfo]);
}
}
});

Resources