4

I have a DataManager class which returns a shared instance:

+ (DataManager *)sharedInstance;
{
    static DataManager *sharedInstance = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred, ^{
        sharedInstance = [[DataManager alloc] init];
    });

    return sharedInstance;
}

In here I keep track of my managedObjectContext, managedObjectModel, persistentStoreCoordinator.

I also have a method where I pull out items for displaying:

- (NSArray *)getItems
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Item"];
    return [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
}

Now in my main app I have a view controller when I call this getItems and then modify items individually. So for example set item.itemName = @"testName"; and then call my save method.

I also have an iOS 8 where in my TodayViewController I also call the getItems method. I have an NSNotification which detects for managedObjectContext saves.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refetchItems) name:NSManagedObjectContextDidSaveNotification object:[[DataManager sharedInstance] managedObjectContext]];

These refetched items does get called but returns the outdated NSManagedObjects. So for example the itemName has not changed to @"testName".

Where am I going wrong? Let me know if you need to see any other code.

Thanks!

Andrew
  • 15,357
  • 6
  • 66
  • 101
Stephen
  • 1,427
  • 1
  • 17
  • 42
  • Your app and your widget run in different processes. If your app changes the core data store, your widget will not get a notification sent from the app. – quellish Sep 19 '14 at 01:54
  • @quellish what is the best way to detect changes then? – Stephen Sep 19 '14 at 08:10
  • 1
    I have exactly the same problem, but have not found solution either. Only I can think of, is to "destory" context and create it again. – Martin Perry Sep 20 '14 at 17:11
  • Are you sure you do not hear any of the `DidChange` notifications? – Léo Natan Sep 20 '14 at 20:42
  • @LeoNatan I am actually hearing the DidChange notifications. Just refetching does not get me the updated object. Weirdly it does detect new objects that I have added or that I have deleted some objects. – Stephen Sep 21 '14 at 02:37
  • @user132490 I am working on a solution for you. As Martin points out, you would have to destroy your context(s), create new ones, and refetch to get the correct behavior. Otherwise you will continue to get stale values, and risk a crash if an object is deleted from the persistent store.If you only reset your context(s), you may still be over-retaining a managed object that has been deleted, and risk a crash. – quellish Sep 21 '14 at 02:45
  • @quellish thanks for working on a solution. looking forward to seeing it – Stephen Sep 21 '14 at 03:27
  • I have also tried [_managedObjectContext refreshObject:act mergeChanges:YES];. It works a bit, but only for data UPDATE, not for adding or deleting data. – Martin Perry Sep 21 '14 at 06:46
  • @user132490 Well.. my last comment was not working 100%. Now I have added [_managedObjectContext reset]; before reading data in extension. Problem with extension is debugging. It is really slow and sometimes, instead of my data, I got snapshot of some old junk, that is no longer valid. But so far I am not able to get rid of this. – Martin Perry Sep 21 '14 at 10:14
  • @MartinPerry yeah debugging is a pain. The refreshObject worked for what I needed it for. Feel free to give as answer and I will accept as correct – Stephen Sep 21 '14 at 15:12

1 Answers1

7

You may try the following for refreshing particular ManagedObject. And if you want to refresh a list of ManagedObject then loop each object and execute the command.

[_managedObjectContext refreshObject:act mergeChanges:YES];

Or for iOS verion 8.3 and above you can make use of the following method for updating all the ManagedObject in the context at once as follows.

[_managedObjectContext refreshAllObjects];

It works a bit, but only for data UPDATE, not for adding or deleting data.

If it is not working, you can add also

[_managedObjectContext reset];

after that, you have to read "reassign" all variables, that you have loaded from your core data store.

Another solution (slower and more ugly)

If the above is not working, another solution would be to delete current context and create it again.

I just set

_persistentStoreCoordinator = nil;
_managedObjectModel = nil;
_managedObjectContext = nil;

I have CoreDataManager class with this properties

@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

And in class I have manually created setters. If I nill all variables, due to setters, they are inited again once I read them outside my core data manager class.

You can improve this by using NSUserDefault store. It is being updated correctly. In main app, if you change smething, set flag in NSUserDefault. In extension, read this and if flag is marked, reset core data. This way, you will save some ticks and make things faster a bit.

For allocation of NSUserDefault (in both apps - extension and main) use this - after that, you can read data from it as usuall and they should be in sync

NSUserDefaults *prefs = [[NSUserDefaults alloc] initWithSuiteName:GROUP_NAME]; //share with extension
Bharath
  • 2,064
  • 1
  • 14
  • 39
Martin Perry
  • 9,232
  • 8
  • 46
  • 114