NSSortDescriptor for NSFetchRequest sorting unexpectedly - objective-c

My entity has a property (sortOrder) that is of the type Decimal(NSDecimalNumber) but when I execute a fetch request using that property as a key, I get back results in a seemingly random order. If I output the value of the property I get really strange values until I get it's intValue.
Example: The first run produces this result. The first value is the raw value of the property. The second is the intValue, the actual value of the property when I created the object - or at least I thought.
85438160 10
74691424 20
Second run...
85333744 10
85339168 20
Third...
85263696 20
85269568 10
What the hell?
Fetch Request:
NSMutableArray *cats = [[NSMutableArray alloc] initWithArray:[CoreDataHelper searchObjectsInContext:#"RestaurantMenuCategory":nil:#"sortOrder":YES:NO:nil]];
Here is the searchObjectsInContext method from my CoreDataHelperClass:
+(NSMutableArray *) searchObjectsInContext: (NSString*)entityName : (NSPredicate *)predicate : (NSString*)sortKey : (BOOL)sortAscending : (BOOL)distinct : (NSString*)distinctProperty
{
RestaurantController *ctrl = [RestaurantController sharedRestaurantController];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:ctrl.managedObjectContext];
[request setEntity:entity];
if(distinct==YES){
[request setReturnsDistinctResults:YES];
}
if(distinctProperty!= nil){
[request setPropertiesToFetch :[NSArray arrayWithObjects:distinctProperty,nil]];
}
if(predicate != nil){
[request setPredicate:predicate];
}
if(sortKey != nil){
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
}
NSError *error;
NSMutableArray *mutableFetchResults = [[[ctrl.managedObjectContext executeFetchRequest:request error:&error] mutableCopy] autorelease];
[request release];
return mutableFetchResults;
}

You cannot use a %d format to print out an NSDecimalNumber.
NSDecimalNumber is an Objective-C object; %d is for printing "int"s. If you use the %d format string, it will probably print something non-useful like the address of the object; use "%#" instead.

Related

Sorting in Core Data with Multiple Values

i have list of brands stored in Core Data, each brand is associated with multiple contents. The contents have a flag called downStatus which is used to denote whether a content is downloaded or not. The following method is used to fetch all the brands from Core data sorted with brand name
-(void)getDownloadedBrands{
AppDelegate *aAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSMutableArray *aBrands = [[NSMutableArray alloc] init];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"Brand" inManagedObjectContext:aAppDelegate.managedobjectcontext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSSortDescriptor *sort=[[NSSortDescriptor alloc]initWithKey:#"brandName" ascending:YES];
//NSSortDescriptor *sort1=[[NSSortDescriptor alloc]initWithKey:#"downStatus" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObjects:sort, nil]];
aBrands =(NSMutableArray *)[aAppDelegate.managedobjectcontext executeFetchRequest:request error:nil];
[request release];
[sort release];
NSLog(#"%#",aBrands);
brands = [[NSMutableArray alloc]initWithArray:aBrands];
aAppDelegate.dbBrandArr = brands;
[self loadGridView];
}
Now i want to sort using the downStatus which is in the Content. So it will be like, downloaded brands in Alphabetical Order and then Un-Downloaded brands in Alphabetical order. The downStatus takes two values 1 for downloaded 0 for not downloaded. Please help.
One way to do it is by making 2 fetchRequests to CoreData using the NSPredicate with value 1/0 on downStatus property and using the same alphabetical sortDescriptor.
Now create a new Array by adding the 0 array to the 1 array. In your code it would look something like this:
-(void)getDownloadedBrands{
AppDelegate *aAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
//2 arrays
NSMutableArray *aBrands0 = [[NSMutableArray alloc] init];
NSMutableArray *aBrands1 = [[NSMutableArray alloc] init];
//set entity en make requests
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Brand" inManagedObjectContext:aAppDelegate.managedobjectcontext];
NSFetchRequest *request0 = [[NSFetchRequest alloc] init];
NSFetchRequest *request1 = [[NSFetchRequest alloc] init];
[request0 setEntity:entity];
[request1 setEntity:entity];
//create sortDescriptor alphabetically
NSSortDescriptor *sort=[[NSSortDescriptor alloc]initWithKey:#"brandName" ascending:YES];
//create predicates on downStatus property
NSPredicate *predicate0 = [NSPredicate predicateWithFormat:#"downStatus == %#", [NSNumber numberWithBool:0]];
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"downStatus == %#", [NSNumber numberWithBool:1]];
//set predicates to the requests
[request0 setPredicate:predicate0];
[request1 setPredicate:predicate1];
//set sortDescriptor to both requests
[request0 setSortDescriptors:[NSArray arrayWithObjects:sort, nil]];
[request1 setSortDescriptors:[NSArray arrayWithObjects:sort, nil]];
//fetch arrays with downStatus 1/0
aBrands0 =(NSMutableArray *)[aAppDelegate.managedobjectcontext executeFetchRequest:request0 error:nil];
aBrands1 =(NSMutableArray *)[aAppDelegate.managedobjectcontext executeFetchRequest:request1 error:nil];
//release requests // NOT USING ARC??
[request0 release];
[request1 release];
[sort release];
//log results
NSLog(#"aBrands0: %#",aBrands0);
NSLog(#"aBrands1: %#",aBrands1);
//add object 0 array to 1 array
NSArray *combinedArray = [aBrands1 arrayByAddingObjectsFromArray:aBrands0];
//copy array to brands
brands = [[NSMutableArray alloc]initWithArray:combinedArray];
//set property on appDelegate
aAppDelegate.dbBrandArr = brands;
//reload tableView
[self loadGridView];
}
-- edit, adding answer for NSDictionary with downStatus key
-(void)getDownloadedBrands{
AppDelegate *aAppDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSMutableArray *aBrands = [[NSMutableArray alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Brand" inManagedObjectContext:aAppDelegate.managedobjectcontext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
NSSortDescriptor *sort=[[NSSortDescriptor alloc]initWithKey:#"brandName" ascending:YES];
//NSSortDescriptor *sort1=[[NSSortDescriptor alloc]initWithKey:#"downStatus" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObjects:sort, nil]];
aBrands =(NSMutableArray *)[aAppDelegate.managedobjectcontext executeFetchRequest:request error:nil];
NSMutableArray *aBrands0 = [[NSMutableArray alloc] init];
NSMutableArray *aBrands1 = [[NSMutableArray alloc] init];
for (NSDictionary *dict in aBrands) {
if ([dict valueForKeyPath:#"downStatus"] == YES)
{
//add to 1 array
[aBrands1 addObject:dict];
}
else
{
//add to 0 array
[aBrands0 addObject:dict];
}
}
//sort arrays
NSArray * array1 = [aBrands1 sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
NSArray * array0 = [aBrands0 sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
NSArray * allArray = [aBrands1 arrayByAddingObjectsFromArray:aBrands0];
//combine arrays
//copy array to brands
brands = [[NSMutableArray alloc]initWithArray:combinedArray];
//set property on appDelegate
aAppDelegate.dbBrandArr = brands;
//reload tableView
[self loadGridView];
[request release];
[sort release];
NSLog(#"%#",aBrands);
}
If you plan to use the fetch result in a UITableViewController, you could use the sectionNameKeyPath of your fetchedResultsController for the downStatus and keep your sort descriptor for the brand name. Can look like this:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"customSectionTitle" cacheName:nil];
Place this in the header file of your NSManagedObject :
-(NSString*)customSectionTitle;
And this in .m :
-(NSString*)customSectionTitle{
if ([downStatus boolValue] == YES) {
return #"Downloaded";
}
else {return #"Not Downloaded";}
}
If you do not plan to use a fetchedResultsController, just sort first by downStatus and then by brand name.

to set default value when core data fetch result is null

I am using simple core data fetch request code.
- (NSMutableArray *) read_cd
{
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"List_CD" inManagedObjectContext:cd_Context];
[request setEntity:entity];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"last_date" ascending:NO];
NSArray *sortDescriptor = [[NSArray alloc] initWithObjects:sd, nil];
[request setSortDescriptors:sortDescriptor];
NSMutableArray *mutableFetchResults = [[cd_Context executeFetchRequest:request error:&error] mutableCopy];
if(mutableFetchResults == nil){
//Handle the error
}
return mutableFetchResults;
}
and I'm using return value like this
MSMutableArray *now_cd = self.read_cd;
cell output for {
cell.label1.text = [[now_cd objectAtIndex:i] cd_title];
cell.label2.text = [[now_cd objectAtIndex:i] cd_auth];
}
I want to set default value when executeFetchRequest is null.
so I can continue to use an existing output module.
I don't know how to make a method like that
[[object objectAtIndex:i] instance]

NSMutableString and (character)-encoding issue: getting \U00fc in case of ü

i am fetching my entity from coreData and saving the result of fetchrequest to an NSMutableString for text to speech.
self.ttsInboxCards = [[NSMutableString alloc] initWithString:#""];
[self.ttsInboxCards appendString:[[entitySetsCards valueForKey:#"cardTitle"] description]];
And in NSLog i am getting this values:
F\U00fcr | schl\U00e4ge | zuh\U00f6ren
which should be:
Für | schläge | zuhören
I have tried a lot of things to get the correct encoding, for example with:
stringWithUTF8String:
and so on, but nothing worked.
How can i prevent this issue?
EDIT 1:
I am getting the values from coreData like this:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"EntitySetsCardsInbox" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *inboxPred = [NSPredicate predicateWithFormat:#"archived == 0 AND cardId != 0"];
[fetchRequest setPredicate:inboxPred];
NSSortDescriptor *sortDescriptor2 = [[[NSSortDescriptor alloc] initWithKey:sortString ascending:sortAsc selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor2, nil] autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *cardTitles = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[self.ttsInboxCards appendString:[[cardTitles valueForKey:#"cardTitle"] description]];
TextToSpeech
if (isSpeaking) {
[vocalizer cancel];
isSpeaking = NO;
}
else {
isSpeaking = YES;
vocalizer = [[SKVocalizer alloc] initWithLanguage:#"de_DE" delegate:self];
[vocalizer speakString:tmp];
}
I wrote:
cardTitles is an array. So, invoking -valueForKey: on it yields an
array built by invoking -valueForKey:#"cardTitle" on each element.
Then you're asking that array for its description. It's very likely to
do formatting of its contents.
brush51 wrote:
componentsJoinedByString: worked and solved the issue with encoding.
now i have to split them correctly so that i can use it cleanly for
text to speech. can you make an answer with that so i can mark this
question as answered and others can find it faster
Make that Caps U with smaller one and try.

Why do I get a NSInvalidArgumentException?

Why do I get a NSInvalidArgumentException with my code below?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Order dish]: unrecognized selector sent to instance
(void)fetchDishRecords: (NSMutableArray*)array predicate: (NSString*)categoryname
{
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Order" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
//set predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Category=%#", categoryname];
[request setPredicate:predicate];
//set sorting
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Dish" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults)
{
// Handle the error.
}
else
{
for(int i=0;i<[mutableFetchResults count];i++)
{
Order *orderObj = [mutableFetchResults objectAtIndex:i];
//NSLog(orderObj.dish);
//NSString* tempStr = orderObj.dish;
[array addObject:orderObj.dish];
}
}
// Save our fetched data to an array
[mutableFetchResults release];
[request release];
[array addObject:orderObj.dish];
Seems to be the culprit...
orderObj is of class Order and the error says it cannot perform selector dish.
Make sure you have a method -(void)dish implemented for your Order class.

displaying in a UITableView CoreData objects ordered in a Set

I have difficulties displaying objects coming from CoreData in a tableView.
I have 2 sorts of entities : Sample and SampleList. What is important to know is that a SampleList has an attribute sampleSet which is a set of samples (entity of Sample)
First I succeeded in displaying every SampleList. Here is viewDidLoad:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SampleList" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastSampleDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle error
}
[self setSampleListArray:mutableFetchResults];
[mutableFetchResults release];
[request release];
Once I click on a row in my tableView, I would like to display in another UITableView every sample from the SampleList selected.
I thought that I could pass to the subview SampleList mySampleList. But then, I don't know what to do with it as it is not organized.
How can I return an ordered array of Sample (ordered by dateSample attribute for example) ?
Thank you for your time !
You can just use NSSet and NSArray methods on sampleSet to get an ordered array:
sortedArray = [[sampleSet allObjects] sortedArrayUsingSelector:#selector(compare:)];
or if you want to specify particular sort descriptors instead of the regular "compare" method:
sortedArray = [[sampleSet allObjects] sortedArrayUsingDescriptors:sortDescriptors];