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.
Related
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.
I have a horizontally and vertically scrollable table. I get the data for the header and first column from the web service(json). I want to sort the data in ascending order and remove duplicate data from both header and the first column. For removing duplicate values I used the following code:
-(void) requestFinished: (ASIHTTPRequest *) request
{
NSString *theJSON = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSMutableArray *jsonDictionary = [parser objectWithString:theJSON error:nil];
headData = [[NSMutableArray alloc] init];
NSMutableArray *head = [[NSMutableArray alloc] init];
leftTableData = [[NSMutableArray alloc] init];
NSMutableArray *left = [[NSMutableArray alloc] init];
rightTableData = [[NSMutableArray alloc]init];
for (NSMutableArray *dictionary in jsonDictionary)
{
Model *model = [[Model alloc]init];
model.cid = [[dictionary valueForKey:#"cid"]intValue];
model.iid = [[dictionary valueForKey:#"iid"]intValue];
model.yr = [[dictionary valueForKey:#"yr"]intValue];
model.val = [dictionary valueForKey:#"val"];
[mainTableData addObject:model];
[head addObject:[NSString stringWithFormat:#"%ld", model.yr]];
[left addObject:[NSString stringWithFormat:#"%ld", model.iid]];
}
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:head];
headData = [[orderedSet array] mutableCopy];
// NSSet *set = [NSSet setWithArray:left];
// NSArray *array2 = [set allObjects];
// NSLog(#"%#", array2);
NSOrderedSet *orderedSet1 = [NSOrderedSet orderedSetWithArray:left];
NSMutableArray *arrLeft = [[orderedSet1 array] mutableCopy];
//remove duplicate enteries from header array
[leftTableData addObject:arrLeft];
NSMutableArray *right = [[NSMutableArray alloc]init];
for (int i = 0; i < arrLeft.count; i++)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int j = 0; j < headData.count; j++)
{
/* NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.iid == %ld", [[arrLeft objectAtIndex:i] intValue]];
NSArray *filteredArray = [mainTableData filteredArrayUsingPredicate:predicate];*/
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.iid == %ld AND SELF.yr == %ld", [[arrLeft objectAtIndex:i] intValue], [[headData objectAtIndex:j] intValue]];
NSArray *filteredArray = [mainTableData filteredArrayUsingPredicate:predicate];
if([filteredArray count]>0)
{
Model *model = [filteredArray objectAtIndex:0];
[array addObject:model.val];
}
}
[right addObject:array];
}
[rightTableData addObject:right];
}
How will I sort the arrays in ascending order?
Please help.
OK, so you have a model object that looks something like this...
#interface Model: NSObject
#property NSNumber *idNumber;
#property NSNumber *year;
#property NSString *value;
#end
Note, I am intentionally using NSNumber and not NSInteger for reasons that will become clear.
At the moment you are trying to do a lot all in one place. Don't do this.
Create a new object to store this data. You can then add methods to get the data you need. Seeing as you are displaying in a table view sectioned by year and then each section ordered by idNumber then I'd do something like this...
#interface ObjectStore: NSObject
- (void)addModelObject:(Model *)model;
// standard table information
- (NSInteger)numberOfYears;
- (NSInteger)numberOfIdsForSection:(NSinteger)section;
// convenience methods
- (NSNumber *)yearForSection:(NSInteger)section;
- (NSNumber *)idNumberForSection:(NSInteger)section row:(NSInteger)row;
- (NSArray *)modelsForSection:(NSInteger)section row:(NSInteger)row;
// now you need a way to add objects
- (void)addModelObject:(Model *)model;
#end
Now to implement it.
We are going to store everything in one dictionary. The keys will be years and the objects will be dictionaries. In these dictionaries the keys will be idNumbers and the objects will be arrays. These array will hold the models.
So like this...
{
2010 : {
1 : [a, b, c],
3 : [c, d, e]
},
2013 : {
1 : [g, h, u],
2 : [e, j, s]
}
}
We'll do this with all the convenience methods also.
#interface ObjectStore: NSObject
#property NSMutableDictionary *objectDictionary;
#end
#implementation ObjectStore
+ (instancetype)init
{
self = [super init];
if (self) {
self.objectDictionary = [NSMutableDictionary dictionary];
}
return self;
}
+ (NSInteger)numberOfYears
{
return self.objectDictionary.count;
}
+ (NSInteger)numberOfIdsForSection:(NSinteger)section
{
// we need to get the year for this section in order of the years.
// lets create a method to do that for us.
NSNumber *year = [self yearForSection:section];
NSDictionary *idsForYear = self.objectDictionary[year];
return idsForYear.count;
}
- (NSNumber *)yearForSection:(NSInteger)section
{
// get all the years and sort them in order
NSArray *years = [[self.obejctDictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
// return the correct year
return years[section];
}
- (NSNumber *)idNumberForSection:(NSInteger)section row:(NSInteger)row
{
// same as the year function but for id
NSNumber *year = [self yearForSection:section];
NSArray *idNumbers = [[self.objectDictionary allKeys]sortedArrayUsingSelector:#selector(compare:)];
return idNumbers[row];
}
- (NSArray *)modelsForSection:(NSInteger)section row:(NSInteger)row
{
NSNumber *year = [self yearForSection:section];
NSNumber *idNumber = [self idForSection:section row:row];
return self.objectDictionary[year][idNumber];
}
// now we need a way to add objects that will put them into the correct place.
- (void)addModelObject:(Model *)model
{
NSNumber *modelYear = model.year;
NSNumber *modelId = model.idNumber;
// get the correct storage location out of the object dictionary
NSMutableDictionary *idDictionary = [self.objectDictionary[modelYear] mutableCopy];
// there is a better way to do this but can't think atm
if (!idDictionary) {
idDictionary = [NSMutableDictionary dictionary];
}
NSMutableArray *modelArray = [idDictionary[modelId] mutableCopy];
if (!modelArray) {
modelArray = [NSMutableArray array];
}
// insert the model in the correct place.
[modelArray addObject:model];
idDictionary[modelId] = modelArray;
self.objectDictionary[modelYear] = idDictionary;
}
#end
With all this set up you can now replace your complex function with this...
-(void) requestFinished: (ASIHTTPRequest *) request
{
NSString *theJSON = [request responseString];
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSDictionary *jsonDictionary = [parser objectWithString:theJSON error:nil];
for (NSDictionary *dictionary in jsonDictionary)
{
Model *model = [[Model alloc]init];
model.cid = [dictionary valueForKey:#"cid"];
model.idNumber = [dictionary valueForKey:#"iid"];
model.year = [dictionary valueForKey:#"yr"];
model.val = [dictionary valueForKey:#"val"];
[self.objectStore addModelObject:model];
}
}
To get the models out for a particular row then just use...
[self.objectStore modelsForSection:indexPath.section row:indexPath.row];
To get the number of sections in the tableview delegate method...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.objectStore numberOfYears];
}
No messing around with the model in the view controller.
Welcome to the MVC pattern.
There's a crap ton of code here but by placing all the code here you can remove all the complex code from your VC.
NSSet keeps only non-duplicate objects within themselves so to keep only unique objects in array you can use NSSet as -
Suppose you have array with duplicate objects
NSArray *arrayA = #[#"a", #"b", #"a", #"c", #"a"];
NSLog(#"arrayA is: %#", arrayA);
//create a set with the objects from above array as
//the set will not contain the duplicate objects from above array
NSSet *set = [NSSet setWithArray: arrayA];
// create another array from the objects of the set
NSArray *arrayB = [set allObjects];
NSLog(#"arrayB is: %#", set);
The output from the above looks like:
arrayA is: (
a,
b,
a,
c,
a
)
arrayB is: {(
b,
c,
a
)}
and to sort a mutable array in ascending order you can use NSSortDescriptor and sortUsingDescriptors:sortDescriptors. Also you need to provide the key on the basis of which array will be sorted.
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"key" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[array sortUsingDescriptors:sortDescriptors];
[sortDescriptor release];
Here you will get what you want.
//sort description will used to sort array.
NSSortDescriptor *descriptor=[[NSSortDescriptor alloc] initWithKey:#"iid" ascending:YES];
NSArray *descriptors=[NSArray arrayWithObject: descriptor];
NSArray *reverseOrder=[arrLeft sortedArrayUsingDescriptors:descriptors];
reverseOrder is your desire output.
there is another way you can sort objects that followed model.
NSArray *someArray = /* however you get an array */
NSArray *sortedArray = [someArray sortedArrayUsingComparator:^(id obj1, id obj2) {
NSNumber *rank1 = [obj1 valueForKeyPath:#"iid"];
NSNumber *rank2 = [obj2 valueForKeyPath:#"iid"];
return (NSComparisonResult)[rank1 compare:rank2];
}];
here sortedArray is our output.
you can replace same things for yr key as well.
This is what I did to sort the header data in ascending order and to remove duplicates from both header and leftmost column. Hope this will help others
NSOrderedSet *orderedSet3 = [NSOrderedSet orderedSetWithArray:head3];
headData3 = [[orderedSet3 array] mutableCopy];
[headData3 sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2)
{
return [str1 compare:str2 options:(NSNumericSearch)];
}];
NSOrderedSet *orderedSet4 = [NSOrderedSet orderedSetWithArray:left3];
NSMutableArray *arrLeft3 = [[orderedSet4 array] mutableCopy];
[leftTableData3 addObject:arrLeft3];
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);
}
In my code, I set array inside a NSMutableDictionary as
if (array.count > 0) {
[self.filters setValue:array forKey:[self getKey:[[NSNumber numberWithInt:indexPath.section] intValue]]];
}
where array is
NSMutableArray *array = [[NSMutableArray alloc] init];
When the receiving code receives it, I tried to join the values in array as
if ([item objectForKey:#"category_filter"] != nil) {
NSArray *array = [NSArray arrayWithObjects:[item objectForKey:#"category_filter"], nil];
NSString *categories = [array componentsJoinedByString:#","];
NSLog(#"value:%#", categories);
}
where item is (NSMutableDictionary *)item
When I see log, I see as
2014-06-24 17:43:12.520 yelp[69744:70b] value:(
"Bagels (bagels)",
"Bakeries (bakeries)"
)
so they are not joined yet. What am I doing wrong here?
As Hot Licks commented,
I had to make change as
NSArray *array = [NSArray arrayWithArray:[item objectForKey:#"category_filter"]];
instead of
NSArray *array = [NSArray arrayWithObjects:[item objectForKey:#"category_filter"]];
Below is my code. When Looping over an NSArray of NSDictionary objects and trying to append to an array I keeping an NSLog which shows the correct result, but the result NSArray only contains the last record.
for (NSDictionary *entry in entries) {
NSString* projID = [entry objectForKey:#"NM_PROJECT"];
NSArray *projectNames = [[NSArray alloc] initWithObjects:projID,nil];
_projectpicker.delegate = self;
_projectpicker.dataSource = self;
NSLog(#"Error : %#", projID);
}
Log Result:
Test1
test2
test3
test4
When appending to NSArray (Projectnames) I was getting last data..
Every each loop iteration you initialise new array, you don't add new object to the array.
To do that you have to create NSMutableArray before loop and add object inside loop:
NSMutableArray *projectNames = [[NSMutableArray alloc] init];
for (NSDictionary *entry in entries) {
NSString* projID = [entry objectForKey:#"NM_PROJECT"];
[projectNames addObject: projID];
_projectpicker.delegate = self;
_projectpicker.dataSource = self;
NSLog(#"Error : %#", projID);
}
NSLog(#"Array : %#", projectNames);
Keep alloc statement outside of your for loop and use a NSMutableArray and then keep on adding to this array as and when required from inside the for loop.
NSMutableArray *projectNames = [[NSMutableArray alloc] init];
for(...)
{
[projectNames addObject: anyObject];
}
Setting:
_projectpicker.delegate = self;
_projectpicker.dataSource = self;
inside a loop doesn't make much sense. You don't need to do it multiple times.
While the other answers accurately describe what you're doing wrong with the array, your current requirement can be achieved more succinctly using KVC:
NSArray *projectNames = [entries valueForKey:#"NM_PROJECT"];
(then you don't need your own loop at all)