Tableview cell is reloaded multiple table cell each time when its loaded - ios

Im making a fetch request as follows,
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:#"PendingShipmentDetails"];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"PendingShipmentDetails" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
then im adding the fetched records to an array as follows within viewDidLoad method...
NSArray *result = [self.managedObjectContext executeFetchRequest:request error:&error];
if (result.count > 0) {
int i;
amountArray = [[NSMutableArray alloc] init];
statusArray = [[NSMutableArray alloc]init];
shipmentReferenceNumberArray = [[NSMutableArray alloc]init];
invoiceNumberArray = [[NSMutableArray alloc]init];
for (i = 0; i < [result count]; i++) {
pending = (NSManagedObject *)[result objectAtIndex:i];
[shipmentReferenceNumberArray addObject:[pending valueForKey:#"shipmentno"]];
[invoiceNumberArray addObject:[pending valueForKey:#"invoice_no"]];
[amountArray addObject:[pending valueForKey:#"amount"]];
[statusArray addObject: [pending valueForKey:#"status"]];
}
} else {
NSLog(#"SORRY");
}
The problem is that, since adding the objects within for loop, the table cell is being populated with same value multiple times each time when Im navigating towards it. However if im adding the elements to array, outside the for loop, only one element is being added and hence only one table cell is visible at anytime. How can I get this issue sorted?

You are going in quite wrong direction
Instead of using for loop you can use enumerateObjectsUsingBlock
[ChildList enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) {
Child = (ChildListModel*)[[ObjectBuilder builder] objectFromJSON:obj className:NSStringFromClass([ChildListModel class])];
if (Child) {
[originalList addObject:Child];
}
}];

Once all the elements are added in array then reload tableView,So it will return the row count as count of the array and will populated data once for each.

+ (NSArray *)fetchCoreData
{
AppDelegate * app = [[UIApplication sharedApplication]delegate];
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"PendingShipmentDetails" inManagedObjectContext:app.managedObjectContext];
[fetchRequest setEntity:entity];
NSError * error;
NSArray * fetchedObjects = [app.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"Saved Data %#", fetchedObjects);
if (fetchedObjects != nil) {
return fetchedObjects;
}else{
return nil;
}
}
Try this ... i have this method fetchCoreData in my modal class. And this works perfectly for me.
Add the fetched data to array.

Related

Error saving data in Core Data (IOS)

My program receives the JSON data from the Web service. Next, the program stores the data in the database using Core Data. If I call the save data after adding each entry, everything works, but very slowly. Keeping 200 entries takes more than one minute.
If I execute saving only once at the end – the program throw exception.
- (void) onLoadMessages:(NSObject*)object {
NSArray *messages = (NSArray*)object;
if (messages==nil) {
[self onError:#"Message array is null"];
return;
}
NSDate *date = [NSDate date];
long now = [date timeIntervalSince1970];
Boolean update = false;
for(int i=0; i<messages.count; i++) {
NSDictionary *m = messages[i];
Message *msg = [[Message alloc]initWithDictionary:m];
if ([self UpdateMessage:msg UpdateTime:now])
update = true;
}
if (update) {
NSError *error = nil;
// Error throw here
if (![self.managedObjectContext save:&error])
[self onError2:error];
}
}
- (Boolean) UpdateMessage:(Message*) msg UpdateTime:(long)now {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Messages" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSString *s = [NSString stringWithFormat:#"%ld", msg.id];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(id=%#)", s];
[fetchRequest setPredicate:pred];
NSError *error;
NSArray *object = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
Boolean result = false;
if (object==nil)
[self onError2:error];
else {
NSManagedObject *m;
if ([object count]==0) {
// Insert new message
m = [NSEntityDescription insertNewObjectForEntityForName:#"Messages"
inManagedObjectContext:self.managedObjectContext];
[m setValue:[NSNumber numberWithLong:msg.id] forKey:#"id"];
[m setValue:[NSNumber numberWithLong:msg.agancy.id] forKey:#"agancy"];
[m setValue:msg.header forKey:#"header"];
[m setValue:msg.keywords forKey:#"keywords"];
[m setValue:[NSNumber numberWithLong:msg.indate] forKey:#"indate"];
[m setValue:[NSNumber numberWithLong:now] forKey:#"updated"];
result = true;
} else {
// Update message
m = [object objectAtIndex:0];
[m setValue:[NSNumber numberWithLong:now] forKey:#"updated"];
}
// Save the context.
// Too long execution
/*NSError *error = nil;
if (![self.managedObjectContext save:&error])
[self onError2:error];*/
}
return result;
}
Help correct the behavior of the program.
With respect,
Alexander.
P.S.
Execution takes place in the main thread.
Field "Id" for table "Messages" indexed.
I solve the problem by adding privateObjectContext!
_privateObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
And replace managedObjectContext on privateObjectContext when updating/inserting data/

Core Data not saving an incremented integer

I'm making a SpriteKit game and I need to save the player's score using Core Data. I have a property with int value that starts off as being set to "5" and increment it x amount of times. I save it then transition to a different scene and fetch it. It shows up un-incremented with the initial value of "5".
I'm new to Core Data so forgive me if this is a stupid question, but how can I get Core Data to take the incrementation in to account? Or Is information being lost when I reference the property and how can I prevent this?
self.score = 5;
self.score++
and then save by calling this method.
AppDelegate.m
-(void) createObject {
Score *scoreEntity = (Score *)[NSEntityDescription
insertNewObjectForEntityForName:#"Score"
inManagedObjectContext:self.managedObjectContext];
SpaceshipScene *spaceshipSceneReference = [[SpaceshipScene alloc] init];
id points = [NSNumber numberWithInteger: spaceshipSceneReference.score];
scoreEntity.points = points;
scoreEntity.playerName = #"Joe";
NSError *error = nil;
// Saves the managedObjectContext
if (! [[self managedObjectContext] save:&error] ) {
NSLog(#"An error! %#", error);
}
}
This is how I call it.
SpaceshipScene.m
AppDelegate *appDelegateReference = [[AppDelegate alloc] init];
[appDelegateReference createObject];
I then fetch it in another class/scene using this method
AppDelegate.m
-(void)fetchObject {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Score"inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Sort fetched data
NSSortDescriptor *sortByPoints = [[NSSortDescriptor alloc] initWithKey:#"points" ascending:NO];
// Put them in an array
NSArray *sortDescriptor = [[NSArray alloc] initWithObjects:sortByPoints, nil];
// Pass the array to the fetch request
[fetchRequest setSortDescriptors:sortDescriptor];
NSError *error = nil;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Problem %#", error);
}
for (Score *s in fetchedObjects) {
NSLog(#" %# %d",s.playerName, [s.points integerValue]);
}
}
This is how I call it in the final scene/class
AppDelegate *appDelegateReference = [[AppDelegate alloc] init];
[appDelegateReference fetchObject];
Hope this will work, after fetching your Score entity , simply assign new values in it don't use insert object for update any value.
[scoreEntity setPoints:[NSNumber numberWithInteger: spaceshipSceneReference.score]];
[scoreEntity setPlayerName:#"Joe"];
then Save the values:
NSError *error = nil;
// Saves the managedObjectContext
if (! [[self managedObjectContext] save:&error] ) {
NSLog(#"An error! %#", error);
}

How to make a conditional request with NSManagedObjectContext?

I'm new to NSManagedObjectContext. I have created an entity Link in my app, which contains a NSString *url.
At some point of my app, I need to insert a new Link in my base, so I simply do this :
Link *link = [NSEntityDescription
insertNewObjectForEntityForName:#"Link"
inManagedObjectContext:self.managedObjectContext];
link.url = myUrl;
But before doing this, I want to check if there is not already a Link in my base with the same url. And I have no idea of how to do so... what should I do?
EDIT : to retrieve data from the base I'm using this method from a tool I found on the web:
// Fetch objects with a predicate
+(NSMutableArray *)searchObjectsForEntity:(NSString*)entityName withPredicate:(NSPredicate *)predicate andSortKey:(NSString*)sortKey andSortAscending:(BOOL)sortAscending andContext:(NSManagedObjectContext *)managedObjectContext
{
// Create fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
// If a predicate was specified then use it in the request
if (predicate != nil)
[request setPredicate:predicate];
// If a sort key was passed then use it in the request
if (sortKey != nil) {
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Execute the fetch request
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
// If the returned array was nil then there was an error
if (mutableFetchResults == nil)
NSLog(#"Couldn't get objects for entity %#", entityName);
// Return the results
return mutableFetchResults;
}
I would like to know how to use it.
Thanks for your help.
The method you provided just searches for a NSManagedObject that matches the attributes in the NSManagedObjectContext and if one exists, it returns it.
But, what you need to implement is called the Find-or-Create pattern, which is discussed in the Core Data programming guide. Basically, you search for an object matching specific criteria and if it exists that object is returned. If that object does not exist you create it.
Core Data Programming Guide
E.g.
+ (NSString *)entityName
{
return NSStringFromClass([Link class]);
}
+ (instancetype)findUsingPredicate:(NSPredicate *)predicate withContext:(NSManagedObjectContext *)context
{
Link * entity;
// Setup the fetchRequest
NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[[self class] entityName]];
fetchRequest.predicate = predicate;
// Credit: #Martin R
[fetchRequest setFetchLimit:1];
// Execute the fetchRequest
NSError *error = nil;
NSArray * matchingLinks = [context executeFetchRequest:fetchRequest error:&error];
// MatchingLinks will only return nil if an error has occurred otherwise it will return 0
if (!matchingLinks)
{
// handle error
// Core data returns nil if an error occured
NSLog(#"%s %#", __PRETTY_FUNCTION__, [error description]);
}
else if ([matchingLinks count] <= 0)
{
// if the count <= 0, there were no matches
NSLog(#"%s Not found", __PRETTY_FUNCTION__);
} else {
// A link with a url that matches the url in the dictionary was found.
NSLog(#"%s Found", __PRETTY_FUNCTION__);
entity = [matchingLinks lastObject];
}
return entity;
}
+ (instancetype)findUsingPredicate:(NSPredicate *)predicate orCreateWithContext:(NSManagedObjectContext *)context
{
Link * entity = [[self class] findUsingPredicate:predicate withContext:context];
if (!entity) {
entity = [[self class] createWithContext:context];
}
return entity;
}
+ (isntancetype)createWithContext:(NSManagedObjectContext *)context
{
return [[self class] alloc] initWithContext:context];
}
- (instancetype)initWithContext:(NSManagedObjectContext *)context
{
Link * entity = [NSEntityDescription insertNewObjectForEntityForName:[[self class] entityName] inManagedObjectContext:context];
return entity;
}
USE CASE:
NSString * url = #"http://www.mylink.com";
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"url = %#", url];
Link * link = [Link findUsingPredicate:predicate orCreateWithContext:self.managedObjectContext];
link.url = url;
You can do it like this (with your method):
AppDelegate *del = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = del.managedObjectContext;
NSString *urlString = #"YOUR URL HERE";
NSMutableArray *fetchedResults = [self searchObjectsForEntity:#"Link" withPredicate:[NSPredicate predicateWithFormat:#"url == %#", urlString] andSortKey:#"url" andSortAscending:YES andContext:managedObjectContext];
BOOL exist = NO;
if(fetchedResults.count >= 1){
exist = YES;
}

Fill NSArray with NSFetchRequest => [array count] wrong

I have a custom NSManagedObject which has several properties. I alloc and init two instances of the object: compare1 & compare2. Then I do a NSFetchRequest to get two of these custom objects and fill their properties in an NSArray structure to display them in a UITableView later.
My problem is, that the UITableView crashes. I did some research in the code and found out, that sometimes the array does not have the full count. When I play with my sliders and text field it works sometimes, but it is (at least for me) not reproducable.
Thank you in advance!
EDIT: The problem is clear now. The object description is complete. I transfer it to an NSArray via NSArray * array = [[NSArray alloc] initWithObjects: object.value1, ..., nil];. Now the strange thing: [array count] gives me a wrong number? Only the values which are != 0 are in the array. Why so? Thank you!
Here is the code of the NSFetchRequest:
-(NSArray *)performFetch
{
if (__managedObjectContext == nil)
{
__managedObjectContext = [(MasterViewController *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
NSError *error = nil;
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entity" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
NSLog(#"FetchedObjects Count: %i", [fetchedObjects count]); //always right
compare1 = [fetchedObjects objectAtIndex:0]; //compare is a custom NSObject
compare2 = [fetchedObjects objectAtIndex:1]; //with several properties (.1 to .17)
NSLog(#"%#",[compare1 description]); //is complete
NSLog(#"%#",[compare2 description]); //is complete
NSArray * valuesS1 = [[NSArray alloc] initWithObjects:compare1.1,__andsoon__compare1.14, nil];
NSArray * valuesB1 = [[NSArray alloc] initWithObjects:compare1.14__andsoon__compare1.17, nil];
NSArray * valuesS2 = [[NSArray alloc] initWithObjects:compare2.1,__andsoon__compare2.14, nil];
NSArray * valuesB2 = [[NSArray alloc] initWithObjects:compare2.14__andsoon__compare2.17, nil];
NSMutableArray * valuesArray1 = [[NSMutableArray alloc] initWithObjects:valuesS1, valuesB1, nil];
NSMutableArray * valuesArray2 = [[NSMutableArray alloc] initWithObjects:valuesS2, valuesB2, nil];
compareArray = [[NSMutableArray alloc] initWithObjects:valuesArray1, valuesArray2, nil];
NSLog(#"Array1 count: %i",[values1 count]); //sometimes (I don't know why)
NSLog(#"Array2 count: %i",[values2 count]); //[values1 count] != [values2 count]
return compareArray; //sometimes returns an array with too less objects so my UITableView crashes
Only the values which are != 0 are in the array
You can't add nil values directly to an array. (nil is usually zero.) If you need to add "empty" values to an array you need to check and insert an NSNull value instead. For example:
NSArray * valuesS1 = [[NSArray alloc] initWithObjects:val1 ? val1 : [NSNull null], val2 ? val2 : [NSNull null], nil];
Edit
The ?: is equivalent to:
if (val1) {
return val1;
}
else {
return [NSNull null];
}
If you want to use #"0" instead of NSNull you can; it can be anything you like as long as it's an object and is not nil.

Load CoreData objects' string property into UIPickerView

Currently I have an entity named "Events" in a CoreData app. "Events" has one string property named "eventName".
In the -(void)viewDidLoad I am trying to Fetch all the "Events" objects and load their "eventName" by alphabetical order into a UIPickerView.
The ultimate end goal is through the use of a textField, buttons and the pickerView being add new objects in and remove unwanted objects out. Basically turning the UIPickerView into a UITableView. Currently I am able to save objects to the CoreData store, but am not able to pull them/their properties out into the UIPickerView.
I am willing and able to share the project source code to anyone who wants it, or is willing to look at it to help out.
thanks
Chris
-(void)update
{
NSMutableArray *array2 = [[NSMutableArray alloc] init];
CDPickerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = [appDelegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"callName" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
NSArray *array = [moc executeFetchRequest:request error:&error];
for (int i=0; i<array.count; i++) {
Event *theEvent = [array objectAtIndex:i];
NSString *StringOne = [NSString stringWithFormat:#"%#",theEvent.callName];
[array2 addObject:StringOne];
}
self.pickerData = array2;
[singlePicker reloadAllComponents];
}
-(IBAction)addCall{
CDPickerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *theEvent = [NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
[theEvent setValue:callField.text forKey:#"callName"];
[context save:&error];
callField.text=#"";
[callField resignFirstResponder];
self.update;
}

Resources