Following method is showing me a warning, but the app is executing as expected. Please could you check the code and tell me what is wrong there? Only if this important to the app, if the warning is not dangering the app, then tell me if I could let this as it is...thank you
The warning is : Incompatible pointer types assigning to 'NSMutableArray *' from 'NSArray *' at the method definition.
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings)
{
ToDoItem * item = evaluatedObject;
NSString* name = item.todoName;
//searchText having length < 3 should not be considered
if (!!searchText && [searchText length] < 3) {
return YES;
}
if ([scope isEqualToString:#"All"] || [name isEqualToString:scope]) {
return ([name rangeOfString:searchText].location != NSNotFound);
}
return NO; //if nothing matches
}]];
}
self.searchResults = [[[self.fetchedResultsController fetchedObjects]
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:
^BOOL(id evaluatedObject, NSDictionary *bindings) mutableCopy];
mutableCopy is a method on many objects for which a mutable and immutable version exists. In the case of things like NSArray, NSString, NSData, etc, calling mutableCopy on one of these instances will return a mutable version containing the same contents as the original object you called the method on.
For example,
NSArray *immutableArray = [NSArray arrayWithObjects:#"foo",#"bar"];
NSMutableArray *mutableArray = [immutableArray mutableCopy];
However, if you don't intend for searchResults to be an NSMutableArray, you should change it's declaration:
#property (nonatomic,strong) NSArray *searchResults
If you don't intend it to be mutable, it should be declared as immutable.
Given your claim that the warning is not effecting the performance of your app, my best guess is that the proper solution would be changing searchResults from NSMutableArray to NSArray.
filteredArrayUsingPredicate returns an immutable NSArray,
and you seem to have declared searchResults as NSMutableArray.
So either
change the declaration of searchResults to NSArray, or
make a mutableCopy before assigning it.
The proper solution depends on whether you need to modify the searchResults later or not.
Related
I have a search mechanism in my application and thus far an object of NSPredicate is used to filter objects.
I'm pretty confused because of the magic happening in the runtime of my program. In my program there is an array with names and another mutable array with filtered names declared as properties.
#property (strong, nonatomic) NSArray *names;
#property (strong, nonatomic) NSMutableArray *filteredNames;
I initialize the array with names when the view is loaded.
self.names = #[
#"Aeron",
#"Brandon",
#"Chris",
#"David",
#"Elvis",
#"Francisco",
#"George",
#"Oliver",
#"Lary",
#"Neythan",
#"Marcus",
#"Phil"
];
Then I setup a table view and its content, install search mechanism (with iOS 8's one, i.e. UISearchController). And here we go, I implement the UISearchResultsUpdating protocol and my updateSearchResultsForSearchController: is presented below.
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString *input = searchController.searchBar.text;
if (input.length > 0) {
[self.filteredNames removeAllObjects];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSLog(#"Got [%#] string.", evaluatedObject);
NSRange range = [evaluatedObject rangeOfString:input options:NSCaseInsensitiveSearch];
return range.location != NSNotFound;
}];
NSArray *matches = [self.names filteredArrayUsingPredicate:predicate];
[self.filteredNames addObjectsFromArray:matches];
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
}
}
So it works perfectly. But I wonder, why do I get an iteration of my array with names inside of the block? I know that blocks are good due to their specification of saving their frame from a stack with all variables and certain values for the moment in past, therefore they can be executed lately. But why does the block iterate this particular array? I tried to declare a new one and initialize it (since I thought it took all collections and their values), but it didn't work.
Thank you in advance!
To filter the array, you have to iterate through every element and test it against the predicate block. I believe the behaviour you're experiencing is intended.
NSArray *arrClient = [[NSArray alloc] initWithObjects:#"record 1", #"record 2", nil];
NSArray *arrServer = [[NSArray alloc] initWithObjects:#"record 1", #"record 3", nil];
On arrServer I would like to apply predicates to filter only those entries that DON'T already exist in arrClient. e.g. in this case record 1 exist in both arrays and shall be ignored, hence only an array with one entry with the "record 3" string shall be returned.
Is this possible?
UPDATE
The answers below are great. I believe I need to give a real example to verify if what I am doing makes sense after all. (I am still giving a compact version below)
Now the clientItems will be of type FTRecord (Core Data)
#interface FTRecord : NSManagedObject
...
#property (nonatomic) NSTimeInterval recordDate;
#end
#implementation FTRecord
...
#dynamic recordDate;
#end
This class below is a holder for parsing json from a REST service. Hence the serverItems we mentioned earlier will be of this type.
#interface FTjsonRecord : NSObject <JSONSerializable>
{
}
#property (nonatomic) NSDate *recordDate;
#implementation FTjsonRecord
- (NSUInteger)hash
{
return [[self recordDate] hash];
}
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[FTjsonRecord self]]) {
FTjsonRecord *other = object;
return [[self recordDate] isEqualToDate:[other recordDate]];
}
else if ([object isKindOfClass:[FTRecord self]]) {
FTRecord *other = object;
return [[self recordDate] isEqualToDate:[NSDate dateWithTimeIntervalSinceReferenceDate:[other recordDate]]];
}
else {
return NO;
}
}
Going with Wain's example, this seems to work fine. Now is this feasible?
Keep in mind that serverItems are just temporary and only used for syncing with server, and will be thrown away. clientItems is the one that remains in place.
UPDATE 2:
This time I am trying Manu's solution:
I have created this method on my Client DBStore, which is called by the predicate.
The reason I can't use containsObject is because the class types in serverItems and clientItems are not the same type.
-(BOOL)recordExistsForDate:(NSDate *)date
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"recordDate == %#", date];
NSArray *arr = [allRecords filteredArrayUsingPredicate:predicate];
if (arr && [arr count] > 0) {
return YES;
} else {
return NO;
}
}
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(FTjsonRecord *evaluatedObject, NSDictionary *bindings) {
return ![store recordExistsForDate:[evaluatedObject recordDate]];
}];
NSSet *set = [[serverRecords items] filteredSetUsingPredicate:predicate];
What worries me about this solution though, is the linear read from my clientItems (allRecords). I am not sure how efficient it is using the predicate on the array, wonder if there is a better way to achieve this.
You can use NSSet to get the union, intersection and difference (minus) with other sets. This more accurately matches what you're trying to do.
NSMutableSet *serverItems = [[NSMutableSet alloc] init];
[arrServerItems addObjectsFromArray:arrServer];
NSSet *clientItems = [[NSSet alloc] init];
[clientItems addObjectsFromArray:arrClient];
[arrServerItems minus:clientItems];
This does remove the ordering information though.
For predicates you can use:
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:#"NOT (SELF IN %#)", arrClient];
depend to the predicate that you want to use:
you can use an array of arguments using this
[NSPredicate predicateWithFormat:<#(NSString *)#> argumentArray:<#(NSArray *)#>];
and build your predicate using the objects in the array
or use a predicate with block
[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
<#code#>
}]
and in the block evluate the object comparing it with the object in your array
a possible check you can do is:
return ![arrClient containsObject:evaluatedObject];
will exclude objects contained in arrClient
containsObject: use 'isEqual:' to compare the objects
I have a custom class extending NSObject. I am maintaining NSMutableArray of this class objects. Here is the situation,
customObject-class {
NSString *name;
int ID;
.....and many other properties;
}
customObjectsArray [
customObject1,
customObject2,
...etc
]
Now I am trying to use filterUsingPredicate to remove objects that has nil names, like below but it returns very few or none objects while I know that there are hundreds of objects that has name not nil or empty. Could someone please tell me what could be wrong here.
[customObjectsArray filterUsingPredicate:[NSPredicate predicateWithFormat:#"name != nil"]];
Why won't you try like this:
NSMutableArray *array=...;
[array filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
CustomObject *customObject=(CustomObject *) evaluatedObject;
return (customObject.name!=nil);
}]];
As I replied to #rdelmar, I found an issue. This predicate was getting called before customObject1's data were actually initialised. I should check the status of data flag that says data has been initialised for this particular object and then apply filter. It worked. If data is not initialised all object's name is off course nil!
Little diifficult to explain but I am trying to use NSPredicate for filtering an array with custom NSManagedObject by ids. I have a server that can send update, delete or add new objects, and I need to control if those objects from the JSON file already exist, if exist just update them or insert to core data if not.
I am using this predicate now :
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"storeId != %#", [jsonFile valueForKey:#"Id"];
Where jsonFile contains unparsed Store objects. But with this predicate, it will give me a huge array, since one id will be unlike some storeId, and next id will match.
Json file is some sort of this :
"Stores":[{
"id":1,
"name":"Spar",
"city":"London"
}
{
"id":2,
"name":"WalMart",
"city":"Chicago"
}];
I am not sure if I understand correctly what you are trying to achieve, but perhaps you can use the following:
NSArray *jsonFile = /* your array of dictionaries */;
NSArray *idList = [jsonFile valueForKey:#"id"]; // array of "id" numbers
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT(storeId IN %#)", idList];
This will give all managed objects that have a storeId that is not equal to any of the ids in the jsonFile array.
The syntax of the predicate is probably off - someone else may suggest a fix - but if you have an array, why not use
- (NSUInteger)indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
since its much easier:
NSInteger textID = ... // you set this
NSInteger idx = [myArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop))
{
NSInteger objIdx = [obj objectForKey:#"id"] integerValue]; // integerValue works for both NSNUmbers and NSStrings
if(objIdx == testID) {
return YES;
*stop = YES;
}
}
I have an NSMutableArray which holds 100 [NSNULL null] type objects.
However some times it contains 1 valid object(it may be a NSString) and 99 [NSNULL null] type objects(it may vary according to the situations).
may i know is there any built in functions to check ,all elements of array contains [NSNULL null] type object or not?(or it does not contains any one valid objects.)
Thanks.
NB: without iterating all elements using loop statements.
You can use NSPredicate to achieve this.
NSMutableArray *allObjects = /* Assume this is your main array */;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self = nil"];
NSArray *nullObjects = [allObjects filteredArrayUsingPredicate:predicate];
if ([nullObjects count] == [allObjects count]) {
// All objects are [NSNull null]
} else {
// Some objects are of different types(may be NSString)
}
Assuming you know the NSString object you are looking for, you can do it really simple:
NSString *needle = /*The NSString you are looking for*/;
NSMutableArray *allObjects = /*You mutable array*/;
BOOL contains = [allObjects containsObject:needle];
Really simple, but it's assuming that you know the object you are looking for, and since you didn't point that out in the question, this answer might be of help to someone.