UISearch bar in two NSMutables arrays - nsdictionary

I see item UISearchBar search two arrays and very much items and not found solution, the problem its similar, have two NSMutablesArrays "subdetail" and "sublista" its show in Cell cell.textLabel and cell.detailTextLabel.
I try UISearchbar but i tray with NSPredicate and not run, try with NSRange and have more errors, i am desperate.PLEASE help me, any comments agree.
This its my code in Search:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSMutableDictionary *playas = [NSMutableDictionary dictionaryWithObjectsAndKeys:sublista, #"nombre", subdetail, #"localidad", nil];
[alldata addObject:playas];
for(NSDictionary *playas in alldata){
NSString *nombre = [playas objectForKey:#"nombre"];
NSRange nombreRange = [[nombre lowercaseString] rangeOfString:[searchText lowercaseString]];
if(nombreRange.location != NSNotFound)
[filteredList addObject:playas];
}
}
Add rest of code .m and .h
https://dl.dropboxusercontent.com/u/6217319/BuscarViewController.h
https://dl.dropboxusercontent.com/u/6217319/BuscarViewController.m
Thanks in advance.
BEST REGARDS

Your code has a lot of issues with it. I don't know which ones are actually breaking it, but any of these issues could be causing severe problems.
First, the code sample you provided doesn't provide declarations for a lot of hte objects you're referencing. No way this could possibly compile as given. If these are properties of the view controller instance, you need to use the accessor methods -- self.alldata or [self alldata], whichever you prefer.
Second, it looks like you're just adding to these properties. If you never reset their contents, every time this method is called, you're going to increase the size of your data set -- looking at your code, possibly recursively.
Third, you try to merge the two datasets together, and then try to search through only one of them. Either don't merge, or search through both seperately and then merge the results. As it is, what you're doing won't work.
Fourth, your table view should really only be displaying one type of data, so you shouldn't need to merge.
Edit:
Based on the code samples you've provided, your entire VC is going to need restructuring. Given how much of your code is written in what appears to be spanish, I'm doing a bit of guesswork at what you're actually doing.
First off, assuming you're using a modern version of xcode, get rid of the #synthesize in the .m file (it's no longer needed anymore). That will cause every single place you're using the actual ivar instead of a proper getter to turn into an error, so you can fix them quickly. iVars will by default use a prefixed underscore of their property name, so you can still access them if you have to -- but only by explicitly accessing the ivar instead of the property.
Second, you should restructure how you handle the data. I don't know where you get your data, but it looks like the various objects are fairly consistent. You should go ahead and either create a new class to hold all the data, or just put them all into a single dictionary. At that point, the 'alldata' property should, in fact, be an array of all valid data. What you should do is then have a filtered list of data (filteredData would be a good name), and you place whatever data matches the search criteria in there. Just remember to either reload the table or update it appropriately as items move into and out of the filtered list.

Related

iOS >> How to Create a "Place Holder" Array for Objects that Were Not Yet Alloc/Init

In a certain view I have a bunch of AVAudioPlayer properties, each one is supposed to be played upon a certain user action. While the selected AVAudioPlayer is played, if another AVAudioPlayer was played - it should stop.
To manage that, I've created an Array that holds all the AVAudioPlayer properties and upon user selection, before playing the selected AVAudioPlayer, I wish to go over all the AVAudioPlayers and stop them.
The problem is that the reasonable place to create the Array is at the beginning (let's say, in ViewDidLoad) and at this point none of the AVAudioPlayer properties went through alloc+init - so if I look at the Array in the debugger it shows as empty (number of objects = 0). Currently, I do the alloc+init action only when the user is actually selecting a certain AVAudioPlayer.
I can do alloc+init for all the AVAudioPlayers at the beginning as well, but that will take resources that are not necessarily required.
Is there a way to create this Array without taking the required resources? i.e. create the array with "empty" objects and later have them be allocated and initiated?
I have a similar situation. What I've been doing is:
NSArray *myArray = [NSArray arrayWithObjects:#"",#"",#"",#""];
in viewDidLoad (if my array is going to have 4 objects). Then as I have the information available, I do:
[myArray replaceObjectAtIndex:myLocationToAddValue withObject:myObject];
I got the idea for this looking at some code in a very old project written in a completely different language by someone else from my company. It seems to be working for what I need, and it does have the added benefit that I can loop through my array and check
if ([[myArray objectAtIndex:i] length] == 0)
to see where I have items already. I should note that the objects I'm adding are going to all be NSStrings - if this loops through and finds an object has been put into the array that isn't a string (or, more generally, doesn't have a "length" method), I'm guessing some nasty stuff would happen, but I haven't checked for that yet.
I'm certain there must be a much better solution, but I'll put it out there since there haven't been any answers to this yet. I figure a sloppy answer that seems to be working is better than no answer.

Setting an objectAtKey string from NSDictionary to an NSString in separate class

I have got information from a URL(JSON); I easily populate my tableView with the text from my dictionary [aCategory objectAtKey:#"names"] for the cell labels. Now, based on the cell name (category), I want to display another table that will ask for the last URL in order to grab the rest of the text, based on that category.
So, i want an ID which is in [aCategory objectAtKey:#"ID"] to save to a string and put in the URL in the next viewController. I am currently trying to generate it using the auto-generated set method to populate the NSString *ID in the target viewcontroller; when i call
[newView setID: [aCategory objectAtKey:#"ID"].
My new view controller's NSString ID says it is not empty, but when i try to check to see if it is indeed "1" or "2" i get something like /p2002 or something. However, in the original class, if i say cell.detailLabelText.text = [aCategory objectAtKey:#"ID"];, the labels correctly show "1" "2" "3".."14" etc....
SO, how can i get that ID from that key into my other viewcontroller class?
I Know it is a valid NSCFString cause i tested it both with isClass AND with the cell's detailLabelText.
I need more detail to be sure about this, but a couple of notes...
NSString is what is called a class cluster, which basically means it is actually a collection of several class different classes that the underlying framework switches between...this means if you are checking it with
isKindOfClass:
you might not be getting the results you are expecting. Check the documentation for isKindOfClass: in NSObject.
However, I am not sure if there is anything to worry about. The reason you are seeing something like '\p2002' (Could the first letter be a u?) could just be the current underlying representation of the string. Sometimes, when the device holds the content of the string in memory, it does not look exactly like "1" or "2". It doesn't mean that there is a problem: it just means that, a deeper level, the way the string is being held in memory is in different form. That is why your label might say "2" but the variable, when you check it in memory, looks different.
(I am guessing that, since you are handling JSON, the string is encoded in a form called UTF-8.) The point is, nothing may wrong at all.
The real question is, is your new view controller loading correctly or not? Maybe in the viewDidLoad: method of your new view controller, if you run something this line:
NSLog(#"%#", stringID);
This will print the value of stringID to the console. If this number is the same as the number of the table cell's label in the previous view controller, everything should have been passed correctly.

Core data refactoring

Hi
Normally all the methods like
'- (NSFetchedResultsController *)fetchedResultsController '
are placed in the code of view controllers. I find it a bit messy to write Data Fetching code along with lifecycle methods or table delegate methods.
So my point is should I refactor the CoreData methods to some other helper class say DataLoader and then call them in view controllers?
Is this a wrong thing to do or am I going to loose some coding benefits of Core Data methods.
I would say moving the fetchedResultsController to a helper class is a good idea.
A problem I encounter often is to get the spelling of attributes right.
For example I do a predicate and want to filter on an attribute called #"isSelected". There is no check by the compiler nor by the linker to check the string isSelected. I will have to double check each line where the string has been used.
A search&replace won't work on the misspellings because I don't know what bugs have been introduced.
When I get the predicate wrong then no results will be fetched. Problem is that I don't know if there are no matching rows or if I have filtered wrong. I will need to check at runtime and that consumes time.
For predicates the saved templates exist, so predicates are not a perfect example. But think about value forKey: and we are at square one.
Now if all the fetchedResultsController are in one file then checking would become easier. At least it reduces the possibility of missing that little misspelling in a far away and rarely used class.
...or am I going to loose some coding benefits of Core Data methods.
I tend to say no, but other please feel free to jump in.
#Mann yes of course you can do that without loosing any coding benefits.....if u don't want to write Data Fetching code in you view Controller do not write there.....Make any other class lets say it DataLoader and write the fetching code in method of this class......and call this method by making the object of DataLoader class ....u will be able to fetch data from database
Hope u get it!

How to manage memory load of static, lazily loaded dictionaries and arrays

Generally, I use static arrays and dictionaries for containing lookup tables in my classes. However, with the number of classes creeping quickly into the hundreds, I'm hesitant to continue using this pattern. Even if these static collections are initialized lazily, I've essentially got a bounded memory leak going on as someone uses my app.
Most of these are arrays of strings so I can convert strings into NSInteger constants that can be used with switch statements, etc.
I could just recreate the array/dictionary on every call, but many of these functions are used heavily and/or in tight loops.
So I'm trying to come up with a pattern that is both performant and not persistent.
If I store the information in a plist, does the iphoneOS do anything intelligent about caching those when loaded?
Do you have another method that might be related?
EDIT - ANSWER EXAMPLE
Based on a solution proposed below, here's what I'm going to work with...
First, add a method to NSObject via category.
- (void)autoreleaseOnLowMemory;
Now, whenever I want to create lazy-loading static array or dictionary in a helper function, I can just use the following pattern...
- (id)someHelperFunction:(id)lookupKey {
static NSDictionary *someLookupDictionary = nil;
if (!someLookupDictionary) {
someLookupDictionary = [[NSDictionary dictionaryWithObjects:X, Y, Z, nil] autoreleaseOnLowMemory];
}
return [someLookupDictionary objectForKey:lookupKey];
}
Now, instead of that static dictionary living until the end of the program, if we're running out of memory it will be released, and only re-instantiated when needed again. And yes, in a large project running on an iphone, this can be important!
PS - The implementation of autoreleaseOnLowMemory is trivial. Just create a singleton class with a method that takes an object and retains it in a set. Have that singleton listen for low memory warnings, and if it gets one, release all the objects in that set. May want to add a manual release function as well.
I generally prefer plists for this just because they're easy to maintain and reuse in different sections of code. If the speed of loading them into an NSDictionary from file is a concern (and check the profiler to be sure) you can always put them into an instance variable which you can release when you get a memory warning.
If you are just doing strings, you could use C arrays.
id keys[] = { #"a" , #"b" , #"c" };
id values[] = { #"1" , #"2" , #"3" };
And if you occasionally need a true NSArray or NSDictionary from that:
[NSArray arrayWithObjects:values count:3];
[NSDictionary dictionaryWithObjects:values forKeys:keys count:3];
A plist will involve a disk hit and xml parsing for each collection. As far as I know only NSUserDefaults are cached.

Objective C Object Functioning & Passing Arrays

I apologise if this has been asked before but I can't find the info I need.
Basically I want a UITableView to be populated using info from a server, similar to the SeismicXML example. I have the parser as a separate object, is it correct to alloc, init an instance of that parser & then tell RootViewController to make it's table data source a copy of the parser's array.
I can't include code because I haven't written anything yet, I'm just trying to get the design right before I start. Perhaps something like:
xmlParser = [[XMLParser alloc] init];
[xmlParser getXMLData];
// Assuming xmlParser stores results in an array called returnedArray
self.tableDataSource = xmlParser.returnedArray
Is this the best way of doing it?
No, you don't want to do this. You don't want your view controller directly accessing the array of the data-model. This would work in the technical sense but it would be fragile and likely to fail as the project scaled.
As the projects grow in complexity, you will want to increasingly wrap your data model object (in this case the xmlParser) in protective layers of methods to control and verify how the data model changes. Eventually, you will have projects with multiple views, multiple view controllers as well as information entering from both the user and URLs. You need to get into the habit of using the data-model object not just a dumb store you dump stuff into but as an active manager and verifier of your data.
In a situation like this I would have my data-model's array completely wrapped by making it a #protected or #private property. Then I would have dedicated methods for fetching or inserting data into the actual array inside the data-model class itself. No objects outside of the data-model should actually have direct access to the array or have knowledge of its indexes.
So, in this case your data-model would have something like:
- (NSString *) textForLineAtIndexPath:(NSIndexPath *) anIndexPath{
//... do bounds checking for the index
NSString *returnString=[self.privateArray objectAtIndex:anIndexPath.row];
if (returnString=='sometest'){
return returnString;
}
return #""; //return an empty string so the reciever won't nil out and crash
}
as well as a setTextForLineAtPath: method for setting the line if you need that.
The general instructional materials do not spend enough (usually none) time talking about the data-model but the data-model is actually the core of the program. It is where the actual logic of the application resides and therefore it should be one of the most complex and thoroughly tested class in your project.
A good data-model should be interface agnostic i.e. it should work with a view based interface, a web based interface or even the command line. It should neither know nor care that its data will be displayed in a tableview or any other interface element or type.
When I start a new project, the first thing I do is comment out the '[window makeKeyAndVisible];' in the app delegate. Then I create my data-model class and test it old-school by loading data and logging the outputs. Only when it works exactly how I wish it to do I then proceed to the user interface.
So, think real hard about what you want the app to do on an abstract level. Encode that logic in a custom class. Isolate the data from all direct manipulation from any other object. Verify all inputs to the data before committing.
It sounds like a lot of work and it is. It feels like overkill for a small project and in many cases it is. However, getting the habit early will pay big dividends very quickly as your apps grow in complexity.
Not quite. You want the data source to be an object that implements the UITableViewDataSource protocol; what I would do in this situation is create an object that implements that protocol and parses XML, so that you can alloc-init it, then set the data source to that object and have it update the table view as appropriate. So based off your code (and assuming you're running within the table view's controller):
XMLParserAndDataSource xpads = [[XMLParserAndDataSource alloc] init];
[xpads getXMLData];
self.tableView.dataSource = xpads;
It's probably a good idea to give this class itself a reference to an NSXMLParser object, so you can use that to parse the XML, then provide convenience methods (like getXMLData) as well as the UITableViewDataSource methods for your own use. (If you go this route, you should also make your XMLParserAndDataSource class implement the more useful of the NSXMLParser delegate methods, and use them as appropriate to update your table view.)
I'm a Mac programmer and not an iPhone programmer; but on the mac,
self.tableDataSource = xmlParser.returnedArray is not correct. You are supposed to either bind the table's content to an Array Controller (if iPhone has one?) or set the datasource outlet to your RootViewController.
In your rootview controller, you would implement the methods:
– tableView:cellForRowAtIndexPath:
– tableView:numberOfRowsInSection:
For – tableView:cellForRowAtIndexPath: you would return a UITableViewCell with the data you received from the XML parsing according to the index path like so:
UITableCell *myCell = [UITableCell new];
myCell.textLabel.text = [parsedXMLArray objectAtIndex:[indexPath indexAtPosition:indexPath.length-1]];
return myCell;
(Something people don't know is that you can use the + new class method on all NSObject subclasses which automatically call alloc/init.)
For – tableView:numberOfRowsInSection just return the count of the data array:
return parsedXMLArray.count;
Can't edit my question nor post replies, can only post my response as answer.
#TechZen: I'm somebody who tries to form analogies, helps me understand. What you're saying is something like: My original idea was like going into the file room & dumping all the originals on my desk to work on where as you suggest the object be more like an organised file clerk who will search through the data for me and only return the specific datum that I need while being the only one with direct access to that data.
Have I understood correctly?
#Tim: What if I later need the parser to get data for something which is not a table? That's why I thought to dump it into an array & let the caller decide what to do with the data. Would you suggest a second object that would supply the data in the newly required form? (Am I sort of one the right track here or way off?)