I'm downloading list of addresses from the webservice(15.000 addresses). It took maybe 800ms to get all addresses and additional 15 seconds to write them to CoreData. Please give some advice where I am wrong:
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = delegate.managedObjectContext;
for (NSDictionary *dict in addresses) {
[self saveAddressesToCoreDataWithDictionary:dict andManagedObject:managedObjectContext];
}
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"alreadyDownloaded"];
And method:
-(void)saveAddressesToCoreDataWithDictionary:(NSDictionary *)dict andManagedObject:(NSManagedObjectContext *)managedObject
{
NSManagedObject *address;
address = [NSEntityDescription insertNewObjectForEntityForName:#"Address" inManagedObjectContext:managedObject];
[address setValue:[[dict objectForKey:#"lat"] stringValue] forKey:#"lat"];
[address setValue:[[dict objectForKey:#"lon"] stringValue] forKey:#"lon"];
[address setValue:[dict objectForKey:#"addressLong"] forKey:#"addressLong"];
[address setValue:[dict objectForKey:#"addressShort"] forKey:#"addressShort"];
NSError *error;
[managedObject save:&error];
}
Figured out!
Problem was that I was saving managedObjectContext everytime, and it should be saved only when iteration is finished.
Just moved this line:
if ([managedObjectContext save:&error] == NO) {
NSAssert(NO, #"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
}
below for loop.
Related
I am new in iOS and I am facing problem regarding to add NSArray in Core Data as an NSString.
I am using code like this:
NSManagedObjectContext *context = [self managedObjectContext];
//Converting array as an string...
AuditIDCoreData=[NSString stringWithFormat:#"%#",idAuditarray];
AuditnameCoreData=[NSString stringWithFormat:#"%#",nameAuditarray];
NSError *error;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Get_Auditnames_User" inManagedObjectContext:context]];
// NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
NSLog(#"Result =%#",results);
NSString *Stringaudit =[NSString stringWithFormat:#"%#",idAuditarray];
ComplareArray=[devices valueForKey:#"auditname"];
ComplareArray2=[devices valueForKey:#"auditid"];
BOOL contains = [ComplareArray2 containsObject:Stringaudit];
if(contains == YES)
{
}
else
{
if (self.device) {
// Update existing device
[self.device setValue:AuditIDCoreData forKey:#"auditid"];
[self.device setValue:AuditnameCoreData forKey:#"auditname"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Get_Auditnames_User" inManagedObjectContext:context];
// NSLog(#"context",newDevice);
[newDevice setValue:AuditIDCoreData forKey:#"auditid"];
[newDevice setValue:AuditnameCoreData forKey:#"auditname"];
}
//NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
Code I have Updated:
NSString *FailString =#"";
NSString *WarningString =#"";
NSManagedObjectContext *context = [self managedObjectContext];
for (int i = 0; i < idarray.count; i++){
if (self.device) {
// Update existing device
[device setValue:Audit forKey:#"auditnameId"];
[device setValue:Passarray[i] forKey:#"checklistid"];
[device setValue:CheckpointNameIDArray[i] forKey:#"checkpointid"];
[device setValue:FailString forKey:#"failreason"];
[device setValue:WarningString forKey:#"warningreason"];
[device setValue:AuditStartDate forKey:#"starttimedate"];
[device setValue:userid forKey:#"userid"];
[device setValue:imageArray[i] forKey:#"auditimage"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"AuditPost" inManagedObjectContext:context];
[newDevice setValue:Audit forKey:#"auditnameId"];
[newDevice setValue:Passarray[i] forKey:#"checklistid"];
[newDevice setValue:CheckpointNameIDArray[i] forKey:#"checkpointid"];
[newDevice setValue:FailString forKey:#"failreason"];
[newDevice setValue:WarningString forKey:#"warningreason"];
[newDevice setValue:AuditStartDate forKey:#"starttimedate"];
[newDevice setValue:userid forKey:#"userid"];
[newDevice setValue:imageArray[i] forKey:#"auditimage"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
}
But it saves whole array in just a single string. I need to store array element one by one in Core Data as String.
How is it possible?
Can do in just simple steps.
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newData;
for (int i = 0; i < array1.count; i++){
newData = [NSEntityDescription insertNewObjectForEntityForName:#"yourEntityName" inManagedObjectContext:context];
[newData setValue:array1[i] forKey:#"sub-entities"];
[newData setValue:array2[i] forKey:#"sub-entities"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}else {
NSLog(#"Data saved successfully ..");
}
Saving multiple array array1 & array2 to the sub-entities of an entity.
You can convert it as follow
NSArray *arrayToBeConvertedInToString = #[#"This", #"is", #"an", #"array"];
NSString * result = [[arrayToBeConvertedInToString valueForKey:#"description"] componentsJoinedByString:#" "];
NSLog(#"Array in string: %#", result);
You can use method explained by Richard G
I ll suggest you to convert it into NSData and store it as transferable in coreData
Converting into NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arrayName];
Storing into CoreData
[newManagedObject setValue:nsData forKey:#"auditid"];
Be sure you have changed datatype to transformable in coredata
I am new in iOS and I am facing a problem regarding to update value of coredata.
For Save
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *device;
if (self.device) {
// Update existing device
[device setValue:GlobalIndexPath forKey:#"key"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
[newDevice setValue:GlobalIndexPath forKey:#"key"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
My code to fetch core data is
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:context]];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
And to update I and using code
NSManagedObject* favoritsGrabbed = [results objectAtIndex:0];
[favoritsGrabbed setValue:#"1" forKey:#"Key"];
Update code not update it add one object.
Note - GlobalIndexPath is a name of string.
But this is not working for me any suggestion. Thanks in Advcance!
You need to save the context every time you make changes to any NSManagedObject and want it to persist. Try this:
NSManagedObject* favoritsGrabbed = [results objectAtIndex:0];
[favoritsGrabbed setValue:#"1" forKey:#"Key"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
Hello i am just creating an sample project in which i have Login and signup class.i created a signup class with all textfields and registered it successfully with coredata.The problem is i need to get the registered user into login class to make it successfull into next page.i have to check whether the user exist or not in my Login class.
Signup.m
#implementation CoreSignup
#synthesize managesObjectContext=_managesObjectContext;
#synthesize managesObjectModel=_managesObjectModel;
#synthesize persistentStoreCoordinator=_persistentStoreCoordinator;
#pragma mark Insert Row In CoreData
-(void)insertSignUpList:(NSMutableDictionary *)details_Ary
{
BOOL isInserted=[self insertRowForSignUp:[details_Ary valueForKey:#"Firstname"] Lastname:[details_Ary valueForKey:#"Lastname"] password:[details_Ary valueForKey:#"Password"] emailid:[details_Ary valueForKey:#"Emailid"] phoneNo:[details_Ary valueForKey:#"Phoneno"] city:[details_Ary valueForKey:#"City"]];
if (isInserted)
{
//inserted Successfully
NSLog(#"inserted Successfully");
}
}
-(BOOL)insertRowForSignUp:(NSString *)firstName Lastname:(NSString *)LastName password:(NSString *)password emailid:(NSString *)emailid phoneNo:(NSString *)phoneNo city:(NSString *)city{
AppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
SignUp *signUpInfo=[NSEntityDescription insertNewObjectForEntityForName:#"SignUp" inManagedObjectContext:context];
signUpInfo.firstName=firstName;
signUpInfo.lastName=LastName;
signUpInfo.password=password;
signUpInfo.phoneno=#([phoneNo intValue]);
signUpInfo.city=city;
signUpInfo.emailid=emailid;
NSError *error;
if (![context save:&error]) {
NSLog(#"Oops coudnt save");
return NO;
}
return YES;
}
-(NSMutableArray*)fetchAll{
AppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSFetchRequest *request=[[NSFetchRequest alloc]init];
NSEntityDescription *entity1=[NSEntityDescription entityForName:#"SignUp" inManagedObjectContext:context];
[request setEntity:entity1];
NSArray *emptyArray=[self.managesObjectContext executeFetchRequest:request error:nil];
NSMutableArray *AllHistory=[NSMutableArray new];
for (SignUp*signUpHistory in emptyArray) {
NSMutableDictionary *tempDict=[[NSMutableDictionary alloc]init];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.firstName] forKey:#"firstname"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.lastName] forKey:#"lastName"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.password] forKey:#"password"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.emailid] forKey:#"emailid"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.city] forKey:#"city"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.phoneno] forKey:#"phoneno"];
[AllHistory addObject:tempDict];
}
return AllHistory;
}
Login.m
#interface ViewController ()<UITextFieldDelegate>
#end
#implementation ViewController
#synthesize JJuser;
#synthesize JJpassword;
#synthesize managesObjectContext=_managesObjectContext;
#synthesize managesObjectModel=_managesObjectModel;
#synthesize persistentStoreCoordinator=_persistentStoreCoordinator;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self jjtext];
[self jjpass];
}
-(void)jjtext{
JJuser.delegate=self;
[JJuser enableMaterialPlaceHolder:YES];
}
-(void)jjpass{
JJpassword.delegate=self;
[JJpassword enableMaterialPlaceHolder:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSMutableArray*)fetchLogin{
AppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSFetchRequest *request=[[NSFetchRequest alloc]init];
NSEntityDescription *entity1=[NSEntityDescription entityForName:#"SignUp" inManagedObjectContext:context];
[request setEntity:entity1];
NSArray *emptyArray=[self.managesObjectContext executeFetchRequest:request error:nil];
NSMutableArray *AllHistory=[NSMutableArray new];
for (SignUp*signUpHistory in emptyArray) {
NSMutableDictionary *tempDict=[[NSMutableDictionary alloc]init];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.firstName] forKey:#"firstname"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.lastName] forKey:#"lastName"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.password] forKey:#"password"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.emailid] forKey:#"emailid"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.city] forKey:#"city"];
[tempDict setObject:[NSString stringWithFormat:#"%#",signUpHistory.phoneno] forKey:#"phoneno"];
[AllHistory addObject:tempDict];
}
return AllHistory;
}
- (IBAction)Login:(id)sender {
AppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSFetchRequest *FindIt=[[NSFetchRequest alloc]init];
NSEntityDescription *entity1=[NSEntityDescription entityForName:#"SignUp" inManagedObjectContext:context];
[FindIt setEntity:entity1];
NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"firstName = \"%#\"",FindIt]];
[FindIt setPredicate:pred];
NSUInteger count = [context countForFetchRequest:FindIt error:nil];
if (count == NSNotFound)
{
UIAlertView*alert=[[UIAlertView alloc]initWithTitle:#"Hello" message:#"No Such id exist" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}else if (count == 1)
{
}
else{
}
}
- (IBAction)Signup:(id)sender
{
UIStoryboard *board = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SignUpViewController*SignUp = [board instantiateViewControllerWithIdentifier:#"SignUp"];
[self.navigationController pushViewController:SignUp animated:YES];
}
#end
You can use a Fetch Request for checking whether the user is already present and if present then if the passwords match
Try this and tell me if it works
-(BOOL)searchDatabaseForUser:(NSString *)username andPassword:(NSString *)password{
AppDelegate *appDelegate =[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"EntityName"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username = %#",username];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *result = [context executeFetchRequest:fetchRequest error:&error];
if([result count]){
for (NSManagedObject *obj in result) {
if([[obj valueForKey:#"password"]isEqualToString:password]){
return YES;
}
}
return NO;
//passwords doesnt match even though username found
}
else {
return NO;
// No such Username found
}
}
Now call this method as to where you want to validate the user should to the next screen or not, Like this
Login.m
-(IBAction)someButtonToCheckLogin:(id)sender{
BOOL shouldUserLogin = [self searchDatabaseForUser:UserNameTextField.txt andPassword:passwordTextField.txt];
if(shouldUserLogin){
//navigate to the next screen.
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Title of the application" message:#"Password mismatch, Please check again" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
I guess this is pretty self explainable. Hope this helps you to understand.
I have my app freezing issue, So i used Instruments to find the issue and found that issues are related to CoreData save and fetch. I have tried background coredata method (parent-child, Notification) but my issue has been not resolved. Also I referred http://martiancraft.com/blog/2015/03/core-data-stack/ but dont know how this method implemeted in my app.
instrument log:
https://www.dropbox.com/s/agjtw1wqubsgwew/Instruments9.trace.zip?dl=0
SAVE to DB
-(void)updateThreadEntityWithSyncDetails:(NSMutableDictionary *)inDictionary
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
// NSManagedObjectContext *writerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// [writerContext setPersistentStoreCoordinator:[sharedDelegate persistentStoreCoordinator]];
// create main thread MOC
// context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// context.parentContext = writerContext;
// NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// contextforThread.parentContext = context;
// [contextforThread performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ThreadInfo"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",[inDictionary valueForKey:#"thread"]];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
for (ThreadInfo *threadInfo in fetchedObjects)
{
if([[inDictionary allKeys] containsObject:#"userEmail"])
{
if([inDictionary valueForKey:#"userEmail"]!=[NSNull null])
{
threadInfo.userEmail=[inDictionary valueForKey:#"userEmail"];
}
}
if([[inDictionary allKeys] containsObject:#"badgeValue"])
{
if([inDictionary valueForKey:#"badgeValue"]!=[NSNull null])
{
threadInfo.badgeValue=[inDictionary valueForKey:#"badgeValue"];
}
}
if([[inDictionary allKeys] containsObject:#"choice4Percentage"])
{
if([inDictionary valueForKey:#"choice4Percentage"]!=[NSNull null])
{
threadInfo.choice4Percentage=[inDictionary valueForKey:#"choice4Percentage"];
}
}
if([[inDictionary allKeys] containsObject:#"choice5Percentage"])
{
if([inDictionary valueForKey:#"choice5Percentage"]!=[NSNull null])
{
threadInfo.choice5Percentage=[inDictionary valueForKey:#"choice5Percentage"];
}
}
}
NSError *error;
if(![context save:&error]) {
NSLog(#"Child error : %#",error);
}
// [context performBlock:^{
// NSError *error;
// if(![context save:&error]) {
// NSLog(#"%#",error);
// }
// }];
// }];
}
FETCH
-(ThreadInfo *)retrieveSolicitationInfoForThreadID:(NSString*)inThreadID;
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ThreadInfo"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:#"userEmail == %#",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:#"threadID == %#",inThreadID];
\
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
if(fetchedObjects.count!=0)
{
ThreadInfo *threadInfo=[fetchedObjects objectAtIndex:0];
return threadInfo;
}
return nil;
}
SYNC
-(void)updateSolicitationWithSyncDetails:(NSDictionary *)inDictionary
{
NSMutableDictionary *paramDict=[NSMutableDictionary dictionaryWithDictionary:inDictionary];
NSString *userEmail=[[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
[paramDict setObject:[NSNumber numberWithBool:NO] forKey:#"isSystemMessage"];
[paramDict setObject:userEmail forKey:#"userEmail"];
if([[inDictionary allKeys] containsObject:#"owned"])
{
BOOL isDuplicate=[[IXDataBaseManager sharedNetworkDataManager] checkForExistenceOfThreadDetailsForSolicitationID:[inDictionary objectForKey:#"solicitation"]];// FETCH
if(!isDuplicate)
{
int randomIndex=[[IXNetworkDataManager sharedNetworkDataManager] getIndexForColorImageForTab:#"OUT"];
[paramDict setObject:message forKey:#"threadDescription"];
[paramDict setObject:[NSNumber numberWithInt:randomIndex] forKey:#"colorCode"];
BOOL isDuplicateVal=[[IXDataBaseManager sharedNetworkDataManager] checkForExistenceOfSolicitationID:[inDictionary objectForKey:#"solicitation"]];// FETCH
[paramDict setObject:message forKey:#"threadDescription"];
ThreadInfo *threadInfo=[[IXDataBaseManager sharedNetworkDataManager] retrieveSolicitationInfoForThreadID:[inDictionary objectForKey:#"solicitation"]];
[paramDict setObject:threadInfo.threadID forKey:#"thread"];
[[IXDataBaseManager sharedNetworkDataManager] updateThreadEntityWithSyncDetails:paramDict];
}
}
}
First, thank you for making the code snippets and the trace available right away. That is very helpful.
So, looking at the trace and the code.
-updateThreadEntityWithSyncDetails: is being called on the main thread and is 33% of the time spent there. Not good.
You are passing nil for error: NEVER do that. Always pass an error pointer and check the results of your call to see if there is an error.
This conditional can be a lot cleaner:
if([[inDictionary allKeys] containsObject:#"userEmail"])
{
if([inDictionary valueForKey:#"userEmail"]!=[NSNull null])
{
threadInfo.userEmail=[inDictionary valueForKey:#"userEmail"];
}
}
Consider:
if (inDictionary[#"userEmail"] != nil && inDictionary[#"userEmail"] != [NSNull null]) {
threadInfo.userEmail = inDictionary[#"userEmail"];
}
Much easier to read.
This re-write of the method will get the work off the main thread:
- (void)updateThreadEntityWithSyncDetails:(NSMutableDictionary*)inDictionary
{
NSString *loginUser = [[NSUserDefaults standardUserDefaults] valueForKey:#"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:[sharedDelegate managedObjectContext]];
[context performBlock:^{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"ThreadInfo"];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"userEmail == %# && threadID == %#",loginUser, inDictionary[#"thread"]];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
for (ThreadInfo *threadInfo in fetchedObjects) {
if (inDictionary[#"userEmail"] != nil && inDictionary[#"userEmail"] != [NSNull null]) {
threadInfo.userEmail = inDictionary[#"userEmail"];
}
if (inDictionary[#"badgeValue"] != nil && inDictionary[#"badgeValue"] != [NSNull null]) {
threadInfo.badgeValue = inDictionary[#"badgeValue"];
}
if (inDictionary[#"choice4Percentage"] != nil && inDictionary[#"choice4Percentage"] != [NSNull null]) {
threadInfo.choice4Percentage = inDictionary[#"choice4Percentage"];
}
if (inDictionary[#"choice5Percentage"] != nil && inDictionary[#"choice5Percentage"] != [NSNull null]) {
threadInfo.choice5Percentage = inDictionary[#"choice5Percentage"];
}
}
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Child error : %#",error);
}
}];
}
BUT it may still block the main thread as I am guessing the bulk of the CPU time is in executing this fetch. The fetch is slow. You have TWO string compares in that fetch. That is bad and should be corrected. If the ThreadID is not a string then reverse the fetch. Otherwise this is just poor data model design and there isn't going to be much help other than fixing that fetch.
Your other really slow point is in -checkForExistenceOfThreadDetailsForThreadID:. You did not post that method but I suspect it is the same issue, your fetch is costing you a tremendous amount of time AND it is on the main queue.
Overall the design of this is poor and needs to be reworked. You are comparing strings in the data store which is one of the slowest ways to retrieve data. You are also on the main thread for things that do not appear to NEED to be on the main thread.
Remember the golden rule, if the data is NOT being manipulated BY the user then the manipulation MUST NOT be on the main queue. No exceptions.
Have you tried to use an separate Context for the operation and merge it with the mainContext after the the save?
E.g.
NSManagedObjectContext * localContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[localContext setPersistentStoreCoordinator:[[self managedObjectContext] persistentStoreCoordinator]];
after your import is done you save your localContext
NSError * error = nil;
if (![[self localContext] save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
This will write your data to the PersistentStore.
Now your mainContext needs to be informed about the changes in the PersistentStore. This is done by observing for a notification.
e.g.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didSaveNotification:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
and this is the method which reacts to the notification:
- (void)didSaveNotification:(NSNotification *)notification
{
// NSLog(#"%s %#\n",__PRETTY_FUNCTION__,[notification userInfo]);
NSManagedObjectContext * mainContext = [self managedObjectContext];
void (^mergeChanges)(void)=^{
[mainContext mergeChangesFromContextDidSaveNotification:notification];
};
if([NSThread isMainThread]) {
mergeChanges();
} else {
dispatch_sync(dispatch_get_main_queue(), mergeChanges);
}
}
Using different NSManagedContext to work on the same PersistentStore is more threadsafe than working with a single NSManagedContext.
//Your Parent context init
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
[_managedObjectContext setUndoManager:nil];
}
return _managedObjectContext;
}
- (NSManagedObjectContext *)childManagedObjectContext
{
if (_childManagedObjectContext != nil) {
return _childManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_childManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_childManagedObjectContext setPersistentStoreCoordinator:coordinator];
[_childManagedObjectContext setUndoManager:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
}
return _childManagedObjectContext;
}
- (void)_mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *savedContext = [notification object];
// ignore change notifications for the main MOC
if (_managedObjectContext == savedContext)
{
return;
}
if (_managedObjectContext.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)
{
// that's another database
return;
}
dispatch_sync(dispatch_get_main_queue(), ^{
[_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
//Way to use the Parent- child context
[UIAppDelegate.childManagedObjectContext performBlock:^{
for (int i=0; i<count; i++) {
//Fill Database and save it in child context first for each entry and save it in child context
[UIAppDelegate.childManagedObjectContext save:nil];
}
dispatch_sync(dispatch_get_main_queue(), ^{
//Finally when u have inserted all entries save in the parent context
[UIAppDelegate.managedObjectContext save:nil];
});
}]
//Your code is huge to understand the logic but if you haven't tried in this way, just give it shot
I try to invoke this method for one time ever the apps is install and launch for the first time.
My idea is to fill the core data table with my data.
How to auto release this method in viewDidLoad?
- (void)insertNewObject:(id)sender
{
if ((firstRun!=YES)) {
NSLog(#"The data was already Added");
}
else {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:[[NSString alloc]initWithFormat:#"new"] forKey:#"name"];
firstRun = NO;
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
You can use NSUserDefaults to verify if it's the first run.
- (void)insertNewObject:(id)sender
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSDictionary *userDefaultsDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], #"firstRun", nil];
[prefs registerDefaults:userDefaultsDefaults];
BOOL firstRun = [prefs boolForKey:#"firstRun"];
if (firstRun==YES) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:[[NSString alloc]initWithFormat:#"new"] forKey:#"name"];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[prefs setBool:NO forKey:#"firstRun"];
[prefs synchronize];
} else {
NSLog(#"The data was already Added");
}
}
You can call this method in your viewWillDisappear or viewDidLoad with [self insertNewObject:nil];