I have been trying all afternoon to get the CoreData portion of my app working and almost have it working as it should. The problem is that the CoreData does not save appropriate value when the app gets terminated (removed from multitasking menu). The value that remains is the intitial value (in my case 0, defined as NSString) I put into the entity instead of the new value the user entered. So the issue is saving the new value PERMANENTLY after the app is closed and not showing the initial value again when the app is loaded
Entity name: Gameinfo
Attribute: score
I am not sure what I am doing wrong. If anyone could help me, it would be appreciated.
Thanks!
-(IBAction)saveTheory1
{
AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Gameinfo"];
NSError *error = nil;
NSArray *someArray = [context executeFetchRequest:request error:&error];
[[someArray objectAtIndex:0] setValue:[self showKey] forKey:#"score"]; // showKey gets the text from UITextField
}
-(IBAction)showTheory1;
{
AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"Gameinfo"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
matches = [objects objectAtIndex:0];
NSLog (#"show us the current value: %#", [matches valueForKey:#"score"]);
}
You are not saving the value at all. You are just assigning it to the Gameinfo object. To actually save it, call save: method on managed object context:
...
[[someArray objectAtIndex:0] setValue:[self showKey] forKey:#"score"];
NSError *saveError = nil;
[[appDelegate managedObjectContext] save:&saveError];
}
It doesnt look like you are calling [context save:error] anywhere. This is what moves your changes into permanent storage.
Related
I am trying to do my Core-data operation on background context.
I insert all my data on the background context but when I fetch on main thread, it doesnt show any data which I already inserted.
I dont know, where am I doing the mistake...:(
Any help appreciate.
This Is what I have tried for insert and the second one is for fetch the data.
- (void)insertNameAndSurnameInDataBase:(NSString *)name surname:(NSString *)surname numbers:(NSArray *)numbers labels:(NSArray *)labels {
AppDelegate *appDel=(AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *saveObjectContext = [appDel saveManagedObjectContext];
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.parentContext = saveObjectContext;
[bgContext performBlockAndWait:^{
NSError *error;
NameAndSurname *nameAndSurname = [NSEntityDescription insertNewObjectForEntityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
NSString *nSurname = [NSString stringWithFormat:#"%# %#",name,surname];
if (nSurname.length == 0) {
nameAndSurname.nameSurname = [numbers objectAtIndex:0];
} else {
nameAndSurname.nameSurname = nSurname;
}
if ([bgContext save:&error]) {
} else {
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname" inManagedObjectContext:bgContext];
// predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nameSurname =[c] %#", nSurname];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *fetchedObjects = [bgContext executeFetchRequest:fetchRequest error:&error];
if([fetchedObjects count] > 0){
[numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
id obj2 = [labels objectAtIndex:idx];
obj = [obj stringByReplacingOccurrencesOfString:#" " withString:#""];
ContactNumber *phoneNumber = [NSEntityDescription insertNewObjectForEntityForName:#"ContactNumber" inManagedObjectContext:bgContext];
phoneNumber.number = obj;
phoneNumber.label = obj2;
phoneNumber.nameAndSurname = fetchedObjects[0];
NSError *error;
if ([bgContext save:&error]) {
} else {
}
}];
}
}];
}
- (NSArray *)fetchNameAndSurname {
AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
_managedObjectContext = [appDel managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"NameAndSurname"
inManagedObjectContext:_managedObjectContext];
[fetchRequest setSortDescriptors:#[[[NSSortDescriptor alloc] initWithKey:#"nameSurname" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)]]];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
return [fetchedObjects valueForKey:#"nameSurname"];
}
The mistake you are making is to not test the many little pieces of this code which could be not working as expected. To troubleshoot something like this, you must start at the beginning and test one piece at a time. The first issue I see is confusion between managed object contexts…
It appears that the method saveManagedObjectContext is a getter for your main managed object context. In English, save is a verb, so that this method name implies that it is saving some managed object context. If I am correct, you should change the name saveManagedObjectContext to maybe mainMangaedObjectContext.
The statement _managedObjectContext = [appDel managedObjectContext] is quite nonstandard, assigning to an instance variable from what appears to be its getter. If, as usual, _managedObjectContext is the instance variable backing the property managedObjectContext, this statement has no effect.
In your first method you use [appDel saveManagedObjectContext], and in your second method you use [appDel managedObjectContext]. It looks like these should be the same context in order for your fetch to work. Are they?
UPDATE:
As you stated in your comment, that fixes the saving, but now you have a performance problem – saving to the persistent store on disk blocks the user interface, which is what your original code was trying to fix.
This is a solved problem. The solution is that your main-thread context, which interfaces the user, should be a child of your background-thread context which interfaces to the persistent store. It is explained nicely with code in this 2015 blog post by Marcus Zarra. For further reading, Chad Wilken has published a slight variation. Both are written in Objective-C for you :)
I am having this strange issue which has been plaguing me all day, i am updating an object in a background thread which is saving correctly
NSFetchRequest *fetchRequest1 = [[NSFetchRequest alloc] init];
NSEntityDescription *entity1 = [NSEntityDescription entityForName:#"STMilestone"
inManagedObjectContext:tmpContext];
[fetchRequest1 setEntity:entity1];
[fetchRequest1 setReturnsObjectsAsFaults:NO];
NSError *error;
NSString *idNum = [obj valueForKey:#"id"];
// NSUInteger TrackerExists = [tmpContext countForFetchRequest:fetchRequest1 error:&error];
[fetchRequest1 setPredicate:[NSPredicate predicateWithFormat:#"identiferNumber = %#", idNum]];
NSArray *logs = [tmpContext executeFetchRequest:fetchRequest1 error:&error];
NSManagedObject *updateObj = [logs objectAtIndex:0];
[updateObj setValue:[NSNumber numberWithInt:20] forKey:#"progress"];
When i then get this object in the same background thread this gets the correct updated value
However when i go to get this updated value on my main thread it returns the old value, then when i clear the cache and reload the app it pulls the correct data
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"STTracker"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSError *error = nil;
NSMutableArray *array = [NSMutableArray arrayWithArray:[context executeFetchRequest:fetchRequest error:&error]];
I think this may be a context issue, my background thread context is being created with this
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
tmpContext.persistentStoreCoordinator = [appDelegate persistenceCoordinator];
and the main thread is
NSPersistentStoreCoordinator *coordinator = [self persistenceCoordinator];
if(coordinator != nil)
{
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
NSUndoManager *undoManager = [[NSUndoManager alloc] init];
[undoManager setGroupsByEvent:NO];
[_managedObjectContext setUndoManager:undoManager];
}
[_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
However i have updated other objects in my app and they are being pulled through, so any help or insight on why this might be happening would be great
This will not work:
NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc] init];
You need to need to init the context with the right concurrency type and assign it the correct parent context (or persistent store coordinator). See the docs
initWithConcurrencyType:
setParentContext:
When child and parent contexts are set up correctly, you an save the child context which does not really write to the persistent store but just "pushes" the changes up to the parent context. The changes are written to the store once you save the parent context again.
Trying to update some Core Data. The data is actually updating "somewhere", but its not saving/updating the db.
- (IBAction)Update:(id)sender {
NSEntityDescription *entityDesc =
[NSEntityDescription entityForName:#"Preferences"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
if ([objects count] == 0) {
// No update, didnt find any entries.
} else {
for (NSManagedObject *obj in objects) {
[obj setValue:_salesPrice.text forKey:#"value"];
if(![context save:&error]){
NSLog(#"Saving changes failed: %#", error);
}
}
}
//[context save:&error];
}
I've tried [context save:&error]; in the commented area, but still no save. I also get no error on save.
You use only 1 NSManagedObjectContext? Your naming convention is not ideal. Usually you would name the entity Preference, since its one object. Try the following code.
CoreDataAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
// This is for completion. Usually you should not get the context from the App Delegate.
// Its better to pass it from the App Delegate to
// the initial view controller via a property (dependency injection).
NSFetchRequest *req = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Preferences class])];
NSError *error = nil;
NSArray *preferences = [context executeFetchRequest:req error:&error];
// Check error
if ([preferences count] == 0) {
// No update, didnt find any entries.
} else {
for (Preferences *preference in preferences) {
[preference setValue:_salesPrice.text forKey:#"value"];
}
}
[context save:&error];
// Check error
In the following code, I explicitly set returnObjectsasFaults as false. Then RIGHT after the request I check to see if objects are fault or not. NSAssert fail.
Perhaps it's because the object is an imageBlob. Perhaps I am missing something? I just want to make sure.
This is a minor issue. If I get rid the nsassert, then my programs will run anyway. Still it sucks.
+(NSFetchRequest * )fetchRequestInContext:(NSString*) entityName:(NSPredicate *) predicate:(NSString*) sortKey:(BOOL) sortAscending
{
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[BGMDCRManagedObjectContextThreadHandler managedObjectContext]];
[request setEntity:entity];
if(predicate != nil)
{
[request setPredicate:predicate];
}
if(sortKey != nil)
{
NSMutableArray * sortDescriptorArray =[self getMoreSearchDescriptorsForEntity:entityName];
[request setSortDescriptors:sortDescriptorArray];
}
//request.fetchLimit = 200; //can be overridden somewhere else
request.returnsObjectsAsFaults = false;
if (entityName == BusinessString)
{
request.relationshipKeyPathsForPrefetching = arrayRelationship;
}
//[request setIncludesSubentities:<#(BOOL)#>
return request;
}
+(NSArray *) searchObjectsInContextEntityName:(NSString*) entityName Predicate:(NSPredicate *) predicate SortKEy:(NSString*) sortKey Booelan:(BOOL) sortAscending
{
NSManagedObjectContext * moc =[BGMDCRManagedObjectContextThreadHandler managedObjectContext];
NSFetchRequest *request = [self fetchRequestInContext:entityName:predicate:sortKey:sortAscending];
NSError *error;
if (entityName==BusinessString)
{
error=nil; //Some code for breakpoint
}
NSArray *fetchedObjects = [moc executeFetchRequest:request error:&error];
for (NSManagedObject * mo in fetchedObjects) {
NSAssert(!mo.isFault, #"For some reason mo is fault");
}
return fetchedObjects;
}
I've encountered the same problem while operating on a child NSManagedObjectContext. I create one as follows
NSManagedObjectContext *workerMOC = nil;
workerMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerMOC.parentContext = self.moc; // this is my main NSManagedObjectContext
Now after that if I do:
[workerMOC performBlock:^{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Company"];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *allCompanies = [workerMOC executeFetchRequest:fetchRequest error:nil];
}];
I get faults in allCompanies. Which of course, just to clarify, does not happen if I execute the fetch request on self.moc.
However, I get appropriate pre-fetched results if I use the approach below:
[workerMOC performBlock:^{
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
fetchRequest.entity = [NSEntityDescription entityForName:#"Company" inManagedObjectContext:workerMOC];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray *allCompanies = [workerMOC.persistentStoreCoordinator executeRequest:fetchRequest
withContext:workerMOC
error:nil];
}];
So it seems to be the case, that fetching on NSManagedObjectContexts tied directly to the NSPersistentStoreCoordinator works just fine. But in case of child NSManagedObjectContexts, which are not tied directly to the store, but rather to the parent context, do not behave as expected.
I could not find anything related to this matter in the Apple docs, but still, I don't think it's a bug.
I've spent many hours searching for an answer for this problem that seems simple.
So these are my parse methods:
-(void)ParseJSON {
NSURL *URL=[[NSURL alloc]initWithString:#"http://www.foo24.com/kokos"];
NSData* data = [NSData dataWithContentsOfURL:
URL];
[self performSelectorOnMainThread:#selector(JSONtoArrays:)
withObject:data waitUntilDone:YES];
}
-(void)JSONtoArrays:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSMutableArray *fetchCustomersArray=[json objectForKey:#"Customers"];
NSMutableArray *fetchOrdersArray=[json objectForKey:#"Orders"];
[self setJSONCustomers:fetchCustomersArray];
[self setJSONOrders:fetchOrdersArray];
}
and this is my fetch from database method:
-(void)fetchOrders{
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSEntityDescription *orderEntity=[NSEntityDescription entityForName:#"OrderEntity" inManagedObjectContext:managedObjectContext];
NSPredicate *NonDelivered =
[NSPredicate predicateWithFormat:#"orderDelivered == NO"];
//setup fetch request
NSFetchRequest *request=[[NSFetchRequest alloc]init];
[request setEntity:orderEntity];
[request setPredicate:NonDelivered];
//fetch
NSError *error;
NSMutableArray *FetchResultsArray=[[managedObjectContext executeFetchRequest:request error:&error]mutableCopy];
[self setDbOrders:FetchResultsArray];
[dbOrders addObjectsFromArray:JSONOrders];
NSLog(#"%#",[[dbOrders objectAtIndex:0]valueForKey:#"orderName"]);
[self addOrders:dbOrders];
}
What I want to do is to retrieve the non-delivered items from Core Data as an array, then take the new parsed array, delete all entries from database, merge the two arrays and put them back in the database . I don't know if there is as simpler way to do this.
I don't know if there is as simpler way to do this,
There is.
Assuming that the array from the internet conains the orders that you want to add to the database, just iterate through these orders and add them to the database.
Simple, right?