Ok, here's my problem. I am synchronizing data from a server via a REST-api. The returned data is in JSON, I loop through it and takes appropriate actions depending on the data. That is, I either store it as a new object, updates the object if it already exists or deletes it if only exists locally.
To achieve this, I collect the IDs from the returned objects when I loop through the JSON. This gives me a index of all the returned objects. I then query my locally stored data to see if it contains any objects that should be deleted (in other words, if the local ID does exists or not in the JSON response).
And here's my issue (sorry for a somewhat lengthy prologue); the NSPredicate that I use only works for certain scenarios and which ones work or fails seems to be random.
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// Array which populates with the IDs from the server
NSMutableArray *arrayOfLogIDS = [[NSMutableArray alloc] init];
/*
Fetching and parsing JSON ... collecting IDs and adding them to the array. See example below;
*/
NSArray *logs = [[json valueForKey:#"Logs"] valueForKey:#"Object"];
// Looping through the logs array
for (NSArray *log in logs) {
[arrayOfLogIDS addObject:[log valueForKey:#"serverID"]];
}
// The NSPredicate
NSPredicate *serverIDS = [NSPredicate predicateWithFormat:#"NOT (serverID IN %#)", arrayOfLogIDS];
// The array which holds the objects that should be deleted
NSArray *logs = [Logs MR_findAllWithPredicate:serverIDS inContext:localContext];
}];
The problem is just that the NSPredicate won't work for this specific circumstance. It returns no results even though I know I have objects locally that should be deleted.
I use this approach in other places in the application, and it works as expected. As you can see I am using Magical Record for Core Data management in this app.
I feel that I have completely run out of things to try next, so any help would be much appreciated! :)
Ok, as it turns out, the array of IDs sometimes had the values stored as string and sometimes as integers. Integers worked well with NSPredicate, strings not so much :) Solved! Thanks all for your time.
On current project I have a set of objects stored using Core Data.
Now I need to add one more field to DataModel.
I've added it, have created new mapping model, set it as current and successfully migrated to new data model. Everything works, but.. order of my data is lost.
For some weird reason previous developers doesn't create any order attribute to Data Model and it was sorted automatically when fetching data (by id I believe..)
After migration to new Data Model using mapping model I have random ordering of my data after each migration.
And some application logic related to order of this data is broken now.
How this issue can be resolved?
It is possible to create order attribute based on old ids?
Or how I can migrate with ids?
Already spent a day with this issue and didn't find the solution.
Thanks a lot!
Alex.
Requested retrieving data code:
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Area" inManagedObjectContext:[self context]];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSError *error;
return [[[self context] executeFetchRequest:request error:&error] mutableCopy];
The simplest way is not rely on some internal ordering. Add sort descriptors to the fetch request when you get access to data.
Also to be sure that your sorted results are processed in the sequential order, use old fashioned loop for (int i = 0; i < array.count; i++) instead of enumerators for (id a in array).
Finally figured out how to resolve this.
I've tried to create custom policy and found that data are passed to it in correct order (like it was stored in the CoreData before migration)
here is how to create custom policy: Core Data versioning and migrating with custom policy
After this things became simpler. I've created sortOrder field and increment it for each entity in the custom policy and set it to new field sortOrder of my entity.
Thanks everyone for help.
Here is the code:
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error {
MyEntity* myEntity = (MyEntity*)[NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName] inManagedObjectContext:[manager destinationContext]];
myEntity.myProp1 = [sInstance valueForKey:#"myProp1"];
//a lot of properties here..
//here is the solution - set sortOrder. Is is the property of custom policy class - one instance of this class is used for each entity migration
area.sortOrder = sortOrder;
int value = [sortOrder intValue];
sortOrder = [NSNumber numberWithInteger:value + 1];
[manager associateSourceInstance:sInstance withDestinationInstance:myEntity forEntityMapping:mapping];
return YES; }
Then I simply sort items on my view.
I want a thorough list regarding comparison between the two. Things I have known:
executeFetchRequest:
Message sent to MOC
Return an array of managed objects
Goal: fetch objects from persistent store to MOC
With table view: has nothing to do with table view
Frequency: often used in a loop, so could be called many many times
performFetch:
Message sent to FRC
After calling it, use fetchedObjects to return an array of managed objects
With table view: FRC is specifically for keeping managed objects and table view rows in sync, and use performFetch to initialize that process.
Frequency: often only once. Unless fetch request of FRC changes, no need to call performFetch a second time
Please correct me if I am wrong and append the list. Thank you.
About executeFetchRequest:
Message sent to MOC
Yes
Return an array of managed objects
Yes, but you can also change the type of results you want to retrieve. In NSFetchRequest you can set a different result type with:
- (void)setResultType:(NSFetchRequestResultType)type
where NSFetchRequestResultType can be of different types. Taken from Apple doc:
enum {
NSManagedObjectResultType = 0x00,
NSManagedObjectIDResultType = 0x01,
NSDictionaryResultType = 0x02
NSCountResultType = 0x04
};
typedef NSUInteger NSFetchRequestResultType;
Goal: fetch objects from persistent store to MOC
Yes, creating a NSFetchRequest and performing a request, it the same as creating a SELECT statement in SQL. If you also use a NSPredicate it's the same as using SELECT-WHERE statement.
With table view: has nothing to do with table view
Yes, but with retrieved data you can populate a table
Frequency: often used in a loop, so could be called many many times
It depends, on what you want to achieve. It could be within a loop or not. Executing the request within a loop could have impact on performance but I would not be worried on that. Under the hood Core Data maintains a sort of cache mechanism. Every time you perform a request, if data are not in the cache, Core Data executes a round trip on your store (e.g. sql file) and populate the cache with the objects it has retrieved. If you perform the same query, the round trip will not performed again due to the cache mechanism. Anyway, you could avoid to execute a request within the run loop, simply moving that request outside the loop.
About performFetch:
Message sent to FRC
Yes
After calling it, use fetchedObjects to return an array of managed
objects
Yes, but you can also retrieve an object with [_fetchedResultsController objectAtIndexPath:indexPath]; if you are populating a specific cell within a table.
Here I really suggest to read a nice tutorial on NSFetchedResultsController
With table view: FRC is specifically for keeping managed objects and
table view rows in sync, and use performFetch to initialize that
process.
Yes, a NSFetchedResultsController works in combination with a NSManagedObjectContext for you. Furthermore, it enables lazy loading of data. Suppose you have 1000 elements you retrieve and you want to display them in a UITableView. Setting a request for a NSFetchRequest like:
[fetchRequest setFetchBatchSize:20];
and using it with an instance of a NSFetchedResultsController, it allows to load 20 elements at first. Then when you scroll, other 20 elements are loaded, and so on. Without a NSFetchedResultsController you must implement this behavior manually. Refer to the tutorial I provided for further info.
Frequency: often only once. Unless fetch request of FRC changes, no
need to call performFetch a second time
It depends on what you want to achieve. Most of the time you could call it once.
Hope that helps.
Edit
You have to call performFetch explicitly. I like to create a property for NSFetchedResultsController in my header file (.h) like
#property (nonatomic, strong, readonly) NSFetchedResultsController* fetchedResultsController;
and synthesize it in your implementation file (.m) like
#synthesize fetchedResultsController = _fetchedResultsController;
Then always within the .m file override the getter to create an new instance of it:
- (NSFetchedResultsController*)fetchedResultsController
{
// it already exists, so return it
if(_fetchedResultsController) return _fetchedResultsController;
// else create it and return
_fetchedResultsController = // alloc-init here with complete setup
return _fetchedResultsController;
}
Once done, within your class (for example in viewDidLoad method) use it like
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
You are comparing the wrong elements. NSFetchedResultsController uses the NSManagedObjectContext to perform the fetch, and under proper configuration, monitors the changes in the managed object context to verify the status of the fetch properties it is monitoring, but the actual fetches are done by the context. On both cases, NSManagedObjectContext does the fetch. The difference being that, using the NSManagedObjectContext directly, you get an NSArray type of object (the actual runtime class is different than an array you get using [NSArray array]), while NSFetchedResultsController has a different purpose (have a collection of results and monitor changes to the records and entity on its fetch request). In other words, NSFetchedResultsController works using the context, but it works different than just a simple collection of objects.
One observation: you shouldn't be using executeFetchRequest inside a loop, especially calling it "many many times". Each fetch has its performance cost. You can call executeFetchRequest once, and do a loop to check the result.
I recently stumbled across a difficult problem..
I'm normally using a NSFetchedResultsController to get Data out of my CoreData, and display it within a TableViewController.
The problem I have now is that I can't get the results I want with a NSFetchRequest right away, because having a m:n relationship and I implemented a Table in Core Data (References) which stores the references to the Objects...
So what I do now is, I use a fetch Request:
NSArray* references = [self.context executeFetchRequest:fetchRequest error:&error];
and then iterate through this array to get my Objects:
for(References* ref in references)
{
Post* tmp = ref.ref_of_post;
}
So is there a way to manually add these objects to my NSFetchedResultsController or is there a possibility to get the wanted object trough a NSFetchRequest?
Thanks for your help.
Never mind that.
I just added these Objects to a NSMutableArray and then would use this Array in the same way as the NSFetchedResultsController..
I have started to work on a core data project. The data to store in the database comes from the server and I am able to store it successfully into coredata. However, each fetch is resulting in duplicate entries. What is the best way to check whether the data exists in core data and append only if the data is not found.
Here is my implementation so far:
for (NSDictionary *dict in array) {
DatabaseManagement *mo = [NSEntityDescription insertNewObjectForEntityForName:#"Subscription" inManagedObjectContext:context];
[mo setValuesForKeysWithDictionary:dict];
The array contains nested dictionary with keys corresponding to my entities attribute names.
I know I can use NSPredicate to achieve this but can someone provide sample code or some resources?