The following code throws an NSInvalidArgumentException on the call to fetchAssetsWithOptions:
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors =
#[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:NO]];
allPhotosOptions.predicate =
[NSPredicate predicateWithFormat:#"pixelHeight >= pixelWidth * 1.95" ];
self.allPhotos = [PHAsset fetchAssetsWithOptions:allPhotosOptions];
The message is
NSInvalidArgumentException ... reason: '*** -constantValue only defined for abstract class.
Define -[NSFunctionExpression constantValue]!'
I tried numerous variations on the predicate format string, but always get this message when I try to use multiplication. What am I doing wrong?
I'm not certain why, but when you don't pass any format arguments to predicateWithFormat: and use it with Photos you get this error;
I was able to fix the issue by ensuring I was passing format arguments. So in your case:
float heightLimit = pixelWidth * 1.95;
[NSPredicate predicateWithFormat:#"pixelHeight >= %f", heightLimit];
Related
In an AudioUnit plugin, I'm using NSFont.
NSFontManager* fontManager = [NSFontManager sharedFontManager];
NSFont* nativefont = [fontManager fontWithFamily:[NSString stringWithCString: fontFamilyName.c_str() encoding: NSUTF8StringEncoding ] traits:fontTraits weight:5 size:fontSize ];
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment : NSTextAlignmentLeft];
NSMutableDictionary* native2 = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
nativefont, NSFontAttributeName,
style, NSParagraphStyleAttributeName,
nil];
// .. later
void someFunction(NSMutableDictionary* native2)
{
float lineGap = [native2[NSFontAttributeName] leading];
Compiler says (about last line): assigning to 'float' from incompatible type 'NSCollectionLayoutSpacing * _Nullable'
NOTE: This has only failed recently since switching to Xcode 11.1, on an older version of XCode it built OK. Any help appreciated.
In your code, the expression native2[NSFontAttributeName] is of unknown type, and therefore of type id. The compiler will let you send an object of type id any message without complaint, but it has no context for determining the type of the message's return value.
You want to get the leading property of NSFont, but the compiler is just picking any leading property selector at random and I'm guessing it has ended up choosing the leading property of NSCollectionLayoutEdgeSpacing which has a return type of NSCollectionLayoutSpacing not float.
I suspect that casting the expression [(NSFont*)(native2[NSFontAttributeName]) leading] would do the trick, but if I were writing this code I'd simply refer to the original (typed) object, since you already have it:
float lineGap = nativefont.leading;
I'm trying to split a string and separate it into variable but am getting the following error:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 ..
1]'
Code:
NSLog(#"%#", strResult);
NSArray* LocInfo = [strResult componentsSeparatedByString: #"|"];
NSString* Response1 = [LocInfo objectAtIndex: 0];
NSString* Response2 = [LocInfo objectAtIndex: 1];
NSString* Response3 = [LocInfo objectAtIndex: 2];
Any ideas? Thanks!
Your strResult is broken into LocInfo array that contains only two elements, and you tried to access third one.
As your string already contains 1 / 2 or 3 NSStrings, therefore no need to again store then into NSString, you can directly use them by LocInfo[index].
If you need to check how many strings are there simply use : [LocInfo count];
I have a subclass of the CoreDataTableViewController (subclass of UITAbleViewController dome by the people on Stanford done to link CoreData and TableViews). On this Class, I want to perform a fecth, sorting by an attribute called "definition" and the code which executes it is the following:
- (void)setupFetchedResultsController{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:self.entity];
request.propertiesToFetch=[NSArray arrayWithObject:#"definition"];
request.returnsDistinctResults=YES;
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"%K != nil", #"definition"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"%K != ''", #"definition"];
NSPredicate *predicate3= [NSPredicate predicateWithFormat:#"%K contains[cd] %#", #"definition", self.seachBar.text];
NSArray *prepredicateArray;
if ([self.seachBar.text length]) {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2, predicate3,nil];
}else {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2,nil];
}
request.predicate=[NSCompoundPredicate andPredicateWithSubpredicates:prepredicateArray];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"definition" ascending:YES ]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
If I understood it correctly, setting request.returnsDistinctResults=YES; should avoid fetching duplicates. However it doesn't work and I'm seeing duplicates of this attribute's value.
Is there something I'm missing there? I'd appreciate some pointings there. Thank you in advance.
EDIT: If anyone is having the same issue here, after applying David's answer the resulting fetchedResultsController is just a NSDIctionary with object with only the requested value, which for displaying only purposes is quite fine. One thing I've done in cellForRowAtIndexPath in order to display the results on the cell label is:
Before:
HNMR *hnmr = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text=hnmr.definition;
After:
cell.textLabel.text=[[self.fetchedResultsController objectAtIndexPath:indexPath] valueForKey:#"definition"];
From the documentation of returnsDistinctResults:
This value is only used if a value has been set for propertiesToFetch.
From the documentation of propertiesToFetch:
This value is only used if resultType is set to NSDictionaryResultType.
From the documentation of resultType:
The default value is NSManagedObjectResultType.
This all tells me that the propertiesToFetch is ignored because you haven't set the resultType yourself and the default it to return managed objects instead of dictionaries. Since the propertiesToFetch is ignored the returnsDistinctResults is ignored as well and thus you are still getting duplicates.
Try setting the result type to return dictionaries instead of managed objects.
request.resultType = NSDictionaryResultType;
In addition to David Rönnqvist answer I suggest a useful link (with a sample) on selecting distinct values with Core Data:
core-data-how-to-do-a-select-distinct
Hope that helps.
I often use Transformable for Core Data attributes, so I can change them later.
However, it seems like, if I want to use NSPredicate to find a NSManagedObject, using "uniqueKey == %#", or "uniqueKey MATCHES[cd] %#", it's not working as it should.
It always misses matching objects, until I change the attributes of the uniqueKey of the matching object to have specific class like NSString, or NSNumber.
Can someone explain the limitation of using NSPredicate with Transformable attributes?
Note: I'm not sure when/if this has changed since 5/2011 (from Scott Ahten's accepted answer), but you can absolutely search with NSPredicate on transformable attributes. Scott correctly explained why your assumptions were broken, but if Can someone explain the limitation of using NSPredicate with Transformable attributes? was your question, he implied that it is not possible, and that is incorrect.
Since the is the first google hit for "Core Data transformable value search nspredicate" (what I searched for trying to find inspiration), I wanted to add my working answer.
How to use NSPredicate with transformable properties
Short, heady answer: you need to be smart about your data transformers. You need to transfrom the value to NSData that contains what I'll call "primitive identifying information", i.e. the smallest, most identifying set of bytes that can be used to reconstruct your object. Long answer, ...
Foremost, consider:
Did you actual mean to use a transformable attribute? If any supported data type -- even binary data -- will suffice, use it.
Do you understand what transformable attributes actually are? How they pack and unpack data to and from the store? Review Non-Standard Persistent Attributes in Apple's documentation.
After reading the above, ask: does custom code that hides a supported type "backing attribute" work for you? Possibly use that technique.
Now, past those considerations, transformable attributes are rather slick. Frankly, writing an NSValueTransformer "FooToData" for Foo instances to NSData seemed cleaner than writing a lot of adhoc custom code. I haven't found a case where Core Data doesn't know it needs to transform the data using the registered NSValueTransformer.
To proceed simply address these concerns:
Did you tell Core Data what transformer to use? Open the Core Data model in table view, click the entity, click the attribute, load the Data Model Inspector pane. Under "Attribute Type: Transformable", set "Name" to your transformer.
Use a default transformer (again, see the previous Apple docs) or write your own transformer -- transformedValue: must return NSData.
NSKeyedUnarchiveFromDataTransformerName is the default transformer and may not suffice, or may draw in somewhat-transient instance data that can make two similar objects be different when they are equal.
The transformed value should contain only -- what I'll call -- "primitive identifying information". The store is going to be comparing bytes, so every byte counts.
You may also register your transformer globally. I have to do this since I actually reuse them elsewhere in the app -- e.g. NSString *name = #"FooTrans"; [NSValueTransformer setValueTransformer:[NSClassFromString(name) new] forName:name];
You probably don't want to use transforms heavily queried data operations - e.g. a large import where the primary key information uses transformers - yikes!
And then in the end, I simply use this to test for equality for high-level object attributes on models with NSPredicates -- e.g. "%K == %#" -- and it works fine. I haven't tried some of the various matching terms, but I wouldn't be surprised if they worked sometimes, and others not.
Here's an example of an NSURL to NSData transformer. Why not just store the string? Yeah, that's fine -- that's a good example of custom code masking the stored attribute. This example illustrates that an extra byte is added to the stringified URL to record if it was a file URL or not -- allowing us to know what constructors to use when the object is unpacked.
// URLToDataTransformer.h - interface
extern NSString *const kURLToDataTransformerName;
#interface URLToDataTransformer : NSValueTransformer
#end
...
// URLToDataTransformer.m - implementation
#import "URLToDataTransformer.h"
NSString *const kURLToDataTransformerName = #"URLToDataTransformer";
#implementation URLToDataTransformer
+ (Class)transformedValueClass { return [NSData class]; }
+ (BOOL)allowsReverseTransformation { return YES; }
- (id)transformedValue:(id)value
{
if (![value isKindOfClass:[NSURL class]])
{
// Log error ...
return nil;
}
NSMutableData *data;
char fileType = 0;
if ([value isFileURL])
{
fileType = 1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value path] dataUsingEncoding:NSUTF8StringEncoding]];
}
else
{
fileType = -1;
data = [NSMutableData dataWithBytes:&fileType length:1];
[data appendData:[[(NSURL *)value absoluteString] dataUsingEncoding:NSUTF8StringEncoding]];
}
return data;
}
- (id)reverseTransformedValue:(id)value
{
if (![value isKindOfClass:[NSData class]])
{
// Log error ...
return nil;
}
NSURL *url = nil;
NSData *data = (NSData *)value;
char fileType = 0;
NSRange range = NSMakeRange(1, [data length]-1);
[data getBytes:&fileType length:1];
if (1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL fileURLWithPath:str];
}
else if (-1 == fileType)
{
NSData *actualData = [data subdataWithRange:range];
NSString *str = [[NSString alloc] initWithData:actualData encoding:NSUTF8StringEncoding];
url = [NSURL URLWithString:str];
}
else
{
// Log error ...
return nil;
}
return url;
}
#end
Transformable attributes are usually persisted as archived binary data. As such, you are attempting to compare an instance of NSData with an instance of NSString or NSNumber.
Since these classes interpret the same data in different ways, they are not considered a match.
you can try this way
NSExpression *exprPath = [NSExpression expressionForKeyPath:#"transformable_field"];
NSExpression *exprKeyword = [NSExpression expressionForConstantValue:nsdataValue];
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:exprPath rightExpression:exprKeyword modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
I have an Array of MKAnnotation objects called arrAnnotations. I want to pick out one of the annotations with the same coordinate as the one stored in a CLLocation object called "newLocation".
I'm trying to use a NSPredicate, but it doesn't work.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(SELF.coordinate == %f)", newLocation.coordinate];
NSArray* filteredArray = [arrAnnotations filteredArrayUsingPredicate:predicate];
[self.mapView selectAnnotation:[filteredArray objectAtIndex:0] animated:YES];
The filteredArray always contains zero objects.
I have also tried the following, which do not work either
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(coordinate == %f)", newLocation.coordinate];
and
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(coordinate > 0)"];
and
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(coordinate.latitude == %f)", newLocation.coordinate.latitude];
The last two crash the app, the third one with an NSInvalidArgumentException for [NSConcreteValue compare:] and the fourth, because latitude is not key-value-coding-compliant (I assume this is because coordinate is just a c-struct and not an NSObject?).
How can I make this work with NSPredicate?
Can anyone give me a link to a document that shows how Predicates work under the hood?
I don't understand what they actually do, even though I have read and understood most of Apple's Predicate Programming Guide.
Is searching a huge array with predicates more efficient than just looping through it with a for...in construct? If yes/no, why?
MKAnnotation protocol's coordinate property is a CLLocationCoordinate2D struct, thus it is not allowed in NSPredicate format syntax according to the Predicate Format String Syntax
You could use NSPredicate predicateWithBlock: instead to accomplish what you are trying to do, but you have be careful with CLLocationCoordinate2D and how to compare it for equality.
CLLocationCoordinate2D's latitude and longitude property are CLLocationDegrees data type, which is a double by definition.
With a quick search you can find several examples of problems you would face when comparing floating point values for equality. Some good examples can be found here, here and here.
Given all that, I believe that using code bellow for your predicate might solve your problem.
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
id<MKAnnotation> annotation = evaluatedObject;
if (fabsf([annotation coordinate].latitude - [newLocation coordinate].latitude) < 0.000001
&& fabsf([annotation coordinate].longitude - [newLocation coordinate].longitude) < 0.000001) {
return YES;
} else {
return NO;
}
}];