Im trying to fetch some objects that Im parsing from a JSON, I dont want to save them until some actions are being done by the user, but I want to fetch them.
Not saving them means I wont be able to fetch them in an different run of the app, but that's ok.
So Im creating the NSManagedObject like this.
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:[self managedObjectContext]];
NSManagedObject *event = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:[self managedObjectContext]];
And then Im trying to fetch it like this.
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Event" inManagedObjectContext:[self managedObjectContext]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"id_server == %#", aIdServer];
[fetchRequest setPredicate:testForTrue];
NSArray *arrayEvents = [[NSArray alloc]initWithArray:[[self managedObjectContext] executeFetchRequest:fetchRequest error:&error]];
id_server is a unique value, and it never returns an event, that is actually there, because if I print all 'Events' in CoreData it shows the event with the id_server.
If I save the context:
NSError *error;
if (![[appDelegate managedObjectContext] save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
It finds the events, but can't I fetch events without saving the context?
Thanks
[EDIT]
The way I access the managedContext is the following:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Even though all attributes were set correctly, since I printed all values and they were correct, I was doing something weird, setting as a NSString something that originally(from the server)is an int.
When I was saving the context, the fetching was working correctly, but when I didnt that, somehow, became an issue.
Changing the type of that attribute, the one I was fectching, solved the issue.
The NSManagedObject class instances that you create will only remain associated with the NSManagedObjectContext that was used to create them until and unless you commit them to persistent store.
So, if you want to fetch the same objects (not saved to persistent store), you will need to have reference to same NSManagedObjectContext instance that was used to create them.
In your code, does [self managedObjectContext] creates a new managedObjectContext every time it is called or it just returns the same context ?
If it creates a new managedObjectContext everytime then you are getting a new instance of NSManagedObjectContext and hence you will not be able to fetch those NSManagedObject instances.
However, if it is returning the same instance of NSManagedObjectContext then you can use
[myFetchRequest setIncludesPendingChanges:YES];
Apple Doc
This matches against currently unsaved changes in the managed object context.
Related
Here is my issue, I am using core data to store around 58 documents. All they have is 4 NSString attributes. I have a helper class that is set up to retrieve documents whenever I need them, however when I passed back the array from my initial getAllDocumentsFromCoreData, all of the attributes seem to be null when accessed in downloadDocumentPDFsAndStoreOnDeviceViaWebService.
The weird thing is when I go to view the array fetched from core data in the getAllDocumentsFromCoreData method, it shows all of the documents/attributes correctly fetched.
What am I doing wrong? I'm relatively new to Core Data, so this could be a rookie mistake.
//USE TO RETRIEVE ALL DOCUMENTS CURRENTLY STORED WITHIN COREDATA
+ (NSArray *) getAllDocumentsFromCoreData
{
CoreData_Helper *helper = [[CoreData_Helper alloc] init];
NSManagedObjectContext *context = [helper managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults: NO];
NSEntityDescription *entity = [NSEntityDescription entityForName: #"Document" inManagedObjectContext: context];
[fetchRequest setEntity: entity];
NSError *error = nil;
NSArray *fetchedDocuments = [context executeFetchRequest: fetchRequest error: &error];
if (error)
{
NSLog(#"%#", [error localizedDescription]);
}
return fetchedDocuments;
}
+ (void) downloadDocumentPDFsAndStoreOnDeviceViaWebService
{
NSArray *fetchedDocuments = [CoreData_Helper getAllDocumentsFromCoreData];
for (Document *document in fetchedDocuments)
{
NSLog(#"%#", [document fileID]);
}
}
This is happening because:
Managed objects don't have strong references to their managed object context
When a managed object context is deallocated, any managed objects fetched from it become inaccessible, with attribute values set to nil, because they no longer have any connection to the persistent store.
In your case you're allocating a managed object context in getAllDocumentsFromCoreData and performing your fetch. You return the results but the context gets deallocated at the end of the function. By the time you look at the returned array, the context is gone and the objects are useless.
You should create the managed object context somewhere else-- probably (though not necessarily) as a property of the object where these methods exist. It's typical to have relatively long-lived context objects rather than create them locally just before performing a fetch. There are various other techniques, but the key in your case is that you must not let the context be deallocated until you're finished with everything you've fetched from it.
I am in a situation where i need to update transformable attribute in my entity in core data, until now i've tried every possible answer from google and stack overflow but did't achieve anything.
This is the method where i am saving object in core data, and my object which i am saving is an NSMutablDictionary type object.
-(void)didSaveToCoreData :(NSMutableDictionary *)newDict
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
DataModelSupport *entity = [NSEntityDescription insertNewObjectForEntityForName:#"CPIEntity" inManagedObjectContext:context];
if (newDict != nil) {
[entity.fixed_Model removeAllObjects];
entity.fixed_Model = newDict;
}
NSError *error ;
[context save:&error];
if(error)
{
NSLog(#"Error in Saving Data");
}
else
{
[self didFetchFromCoreDataModel];
NSLog(#"Successfully saved");
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
in this method i am saving a dictionary object of 19 key/value, at the first time and i am fetching it correctly in didFetchFromCoreDataModel method, but when i refresh the data and get dictionary of 18 key/value i save that dictionary in core data using the same method didSaveToCoreData and fetch it in the same way from didFetchFromCoreDataModel but it still show 19 key/value
DataModelSupport is a subclass of NSManagedObject.
In DataModelSupport.h:
#property (nonatomic,weak) NSMutableDictionary *fixed_Model;
In DataModelSupport.m:
#dynamic fixed_Model;
This is it for the DataModelSupport class.
Now here in this method i am fetching the same object form core data
-(void)didFetchFromCoreDataModel
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setReturnsDistinctResults:YES];
[request setReturnsObjectsAsFaults:NO];
[request setResultType:NSDictionaryResultType];
[request setEntity:entity];
NSError *error ;
NSArray *arr = [context executeFetchRequest:request error:&error];
updatedfinalArr = [arr valueForKey:#"fixed_Model"];
if(error)
{
NSLog(#"Error");
}
else
{
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
And this is how my core data looks like:-
Any help is appreciated.
EDIT
I've implemented some changes in my code now in didSaveToCoreData method i am using this line of code to fetch the Entity by name
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
by this i am not creating new entity every time i call didSaveToCoreData method.
and this is how i am saving NSMutlableDictionary object
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
[entity.fixed_Model removeAllObjects]
entity.fixed_Model = newDict;
but still i am not getting correct result.
now when i refresh the data and save it using the above procedure explained in EDIT section, and fetch it, i get the updated data but it increase the number of objects, like on first attempt when i fetch i got 1 object in array, and on second attempt i got 2 objects and it goes like this, so when ever new data is added its not updating it but instead it add it in the entity s fixed_Model attribute and increase the number of object.
Lastly now i am using this line of code to get the last and update object from array in didFetchFromCoreDataModel method
NSDictionary *ddd = [[arr valueForKey:#"fixed_Model"]lastObject];
updatedfinalArr = [NSMutableArray arrayWithObject:ddd];
Your save method creates a new CPIEntity object each time. So, unless you delete the old object elsewhere in your code, I suspect your fetch is returning several objects, the first of which has the dictionary with 19 key/value pairs in the fixed_Model attribute, and the second/subsequent objects contain the 18 key/value pairs.
When you save, you should try to fetch the existing object first, and if you get zero results then create a new object. Then set the fixed_Model attribute of the new/existing object to your new dictionary.
EDIT
You are still inserting a new object each time (DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];). See below for an example of "fetch or create":
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
request.entity = descriptor;
NSError *error;
NSArray *results = [context executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
entity.fixed_Model = newDict;
} else {
// At least one object saved. There should be only one
// so use the first...
DataModelSupport *entity = [results firstObject];
entity.fixed_Model = newDict;
}
}
I've assumed for simplicity that newDict is not nil; amend as appropriate to handle that case.
Can you narrow down the problem?
Ie. can you compare the two Dictionaries..the original one with 19 values and the new one with 18 values?
Is there a particular entry which is not being 'removed'? That might point to a challenge with 'delete' (or the lack there of).
Alternatively, if you completely replace the content, what result do you get on fetch?
I have a problem with Core Data, because I don't know the best way to handle my problem:
I load a json from a server and parse the results in ManagedObjects. At first the ManagedObjects should be temporary.
But the user can put a ManagedObject to a leaflet. And then the ManagedObject should be saved to CoreData. (The object should be accessible offline)
When the user load the same objects some time later from the server, already saved ManagedObjects should be fetched.
So I don't want to put every object in CoreData / PersistantStore the user doesn't need.
First what I do is to create a background context:
__block NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.parentContext = context;
With a fetch I check if there is already a ManagedObject in the persistantstore.
If there is one, I will take this. else create a new ManagedObject in nil context.
NSArray *results = [backgroundContext executeFetchRequest:fetch error:&error];
if (!error && results.count == 1) {
myModel = [results objectAtIndex:0];
}
else {
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyModel" inManagedObjectContext:backgroundContext];
myModel = (MyModel *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
}
And I do the same with every relationship:
if (! myModel.relation) {
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Relation" inManagedObjectContext:backgroundContext];
myModel.relation = (Relation *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:myModel.managedObjectContext];
}
Works fine so far with creating the models.
But how to save one model?
The managedObjectContext is nil. If I call save: on the managedObjectContext, it saves everything.
In my AppDelegate i wrote a function to insert a ManagedObject in the main ManagedObjectContext:
- (void)insertObjectAndSave:(NSManagedObject *)managedObject {
if (!managedObject.managedObjectContext) {
[self.managedObjectContext insertObject:managedObject];
}
[self saveContext];
}
Is this a good solution? or is there a better way to save temporary ManagedObjects in the main ManagedObjectContext?
Excellent Answered My Mundi..
Here is on more scenario to create NSManagedObject temporary, whether we can make it permanents If we want.
Create temporary object
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
Code this if you wants to save it permanently
[managedObjectContext insertObject:unassociatedObject];
NSError *error = nil;
if (![managedObjectContext save:&error])
{
//Respond to the error
}
You could not create the objects with nil context but with a "temp" context. If you want to save them, call [tempContext save:nil];. If you want to discard them, just throw away the context.
Another strategy is to avoid the complexity of multiple context altogether by adding a simple boolean attribute temp to your entity (if relationships are one-to-many, only the top level entity needs to have this). You can save by default and display only non-temp items. You can delete temp items, including all related items, immediately or periodically.
I am making an application where you need to log in with a 4 digit password but there can only be one password at a time. I am trying to save it to core data but whenever the user adds a new password it just adds it to the long list. How can I restrict an entity to only have one instance of itself?
Here is my code just in case it will help:
-(BOOL)savePassword:(NSString*)password{
AppDelegate * appDelegate = [[AppDelegate alloc]init];
NSManagedObjectContext * context = [appDelegate managedObjectContext];
AppData * appData = (AppData*)[NSEntityDescription insertNewObjectForEntityForName:#"AppData" inManagedObjectContext:context];
appData.password = password;
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AppData" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"There was an error:%#",error);
}
for (AppData * adata in fetchedObjects) {
NSLog(#"Password:%#",adata.password);
}
return YES;
}
Thanks!
The right approach here is to not put this data in Core Data. If you only have one instance, there's no point in using Core Data to solve the problem. There's no benefit to using Core Data for this. Put it somewhere else. Code solutions miss the point, because even if it works, it's a bad design.
You should do like this, first create fetch request and execute a fetch. check if object exist, update data. else if no data exist create an object and save it.
If name of entity which is storing password.
Your code should look like this
AppData * appData;
NSManagedObjectContext * context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"AppData" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if(fetchObjects.count > 0){
appData = [fetchObjects objectAtIndex:0];//assume there will be one object
// and do reset of thing
}
else{
appData = (AppData*)[NSEntityDescription insertNewObjectForEntityForName:#"AppData" inManagedObjectContext:context];
}
appData.password = password;
// save moc here
[context save:nil];
I am working on an iOS application where I am using Core Data for storage. In my store, every entity will be unique, and I'm building a function where I replace one existing entity with another that I pass in. Here is an example of an entity that I pass:
NSManagedObjectContext *context = [[MyDB sharedInstance] managedObjectContext];
User *user = [NSEntityDescription insertNewObjectForEntityForName:#"User" inManagedObjectContext:context];
NSNumber *userNumber = 12345;
user.id = userNumber;
user.name = #"John Doe";
user.email = #"john#doe.net";
user.createdDate = [NSDate date];
[[MyDB sharedInstance] updateUser:user];
Inside my Core Data storage, I have an identical Entity already, except that the email address is "john#doe.com". My update at the moment looks like this:
-(void)updateUser:(User *)user {
NSError *error;
NSManagedObjectContext *context = [[MyDB sharedInstance] managedObjectContext];
// Create fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Create predicate
NSPredicate *pred = [NSPredicate predicateWithFormat:#"id == %#", user.id];
[fetchRequest setPredicate:pred];
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
// handle fetch error
} else {
user = [[User alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
for (User *recordToDelete in results) {
[context deleteObject:recordToDelete];//record gets deleted here, which is fine
}
[context save:&error]; //this doesn't save the new entity that I passed in
if (error) {
// handle save error
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id=%#", 12345];
[fetchRequest setPredicate:pred];
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
for (User *testObject in items) {
NSLog(#"ID: %#, Name: %#, Email: %#, Created Date: %#", [testObject id], [testObject name], [testObject email], [testObject createdDate]);
}
}
The problem is that the above function deletes the existing record in the store, however, it fails to add the new entity that replaces it. How can I correct this?
Also, i think you don't clearly understand what is NSManagedObjectContext. It's something like in-memory object cache. So, if you create object in context, it is tied to context. Object has reference to context, so passing context with object is not necessary - object's context can be obtained from it. Also, contexts and objects are not thread-safe - you cannot pass managed objects between threads and use same context in different threads. Instead, you have to: 1) Create context for every thread
2) If you need to pass something between threads, pass object.objectId from one thread, and in another thread do [context objectWithID:]. It's extremly fast, efficient and safe.
You are not calling save method on managed object context. Call save method on managedObjectContext in which you are creating new object. [managedObjectContext save:nil];
Recmonded way is. First fetch object depending on number, and delete it. After that create managed object. At the end call Save on context.