Search code examples
iosswiftnsarraynsdictionaryuisearchcontroller

Implementing Search Functionality


hello I have implemented an auto complete search on my app. I have cities stored in my mysql database and in app when user types any character or word, the app fetches result from the database and shows it. Now there is a small programming problem I am having and I don't know how to solve it.

The problem is in the same Array in which I am getting a City, I am getting country name and state name as well. As I have implemented a search only on cities not on state and country, I actually need the other columns(state,country) of those rows which are displaying based on user search city. I'll paste the code here for better understanding

class  CityTableViewController: UITableViewController, UISearchResultsUpdating {
    var dict = NSDictionary()
    var filterTableData = [String]()
    var resultSearchController = UISearchController()


    var newTableData = [String]()


    override func viewDidLoad() {
        super.viewDidLoad()



        self.resultSearchController = ({

            let controller  = UISearchController(searchResultsController: nil)
            controller.searchResultsUpdater = self
            controller.dimsBackgroundDuringPresentation = false
            controller.searchBar.sizeToFit()
            self.tableView.tableHeaderView = controller.searchBar
            return controller


        })()

        self.tableView.reloadData()
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {


        if(self.resultSearchController.active){

            return self.filterTableData.count
        }else {

            return dict.count
        }



    }



        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

            let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CountryTableViewCell

            if(self.resultSearchController.active){

                cell.cityNameLabel.text = filterTableData[indexPath.row]

                cell.countryNameLabel.text =  get the country name

                 cell.stateNameLabel.text =  get stateName
                return cell

            }else{

                cell.cityNameLabel.text = (((self.dict["\(indexPath.row)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String
                return cell
            }



        }

        func updateSearchResultsForSearchController(searchController: UISearchController) {
            filterTableData.removeAll(keepCapacity: false)
            let searchWord = searchController.searchBar.text!

            getCityNamesFromServer(searchWord)

            let searchPredict = NSPredicate(format: "SELF CONTAINS[c] %@", searchController.searchBar.text!)
            print("searchPredict is \(searchController.searchBar.text!)")

            for var i = 0; i < self.dict.count; i++ {
                let cityName = (((self.dict["\(i)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String

         let countryName = (((self.dict["\(i)"] as?NSDictionary)!["Country"] as?NSDictionary)!["name"] as?NSString)! as String

let stateName = (((self.dict["\(i)"] as?NSDictionary)!["State"] as?NSDictionary)!["name"] as?NSString)! as String
            newTableData.append(cityname)
            }

            let array = (newTableData as NSArray).filteredArrayUsingPredicate(searchPredict)
            print("array is\(array)")
            filterTableData = array as! [String]
            self.tableView.reloadData()
        }






        func getCityNamesFromServer(searchWord:String){


            let url:String = "http://localhost/"
            let params = ["city":searchWord]



            ServerRequest.postToServer(url, params: params) { result, error in

                if let result = result {
                    print(result)

                    self.dict = result

                }
            }

        }

    }

If I try to setup new array of state and country then data doesn't shows up correctly. cities don't belong to his own state shows up. So How I can keep the order correctly.

Array:

dict

 0 =     {
        City =         {
            code = 10430;
            "country_id" = 244;
            id = 8932;
            name = Laudium;
            "state_id" = 4381;
            "updated_at" = "<null>";
        };
        Country =         {
            id = 244;
            name = "South Africa";
        };
        State =         {
            "country_id" = 244;
            id = 4381;
            name = Gauteng;
        };
    }; etc

newTableData

["Lynnwood", "Lyndhurst", "Laudium"] etc

filterTableData

["Laudium", "La Lucia", "Lansdowne"] etc

Solution

  • You should search the dictionary for the matches and store the matched keys in an array and reference to these keys in the results.

    func updateSearchResultsForSearchController(searchController: UISearchController) {
    
        let searchWord = searchController.searchBar.text!
    
        getCityNamesFromServer(searchWord)
    
        self.filteredKeys.removeAll()
    
        for (key, value) in self.dict {
            let valueContainsSearchWord: Bool = (((value as? NSDictionary)?["City"] as? NSDictionary)?["name"] as? String)?.uppercaseString.containsString(searchWord.uppercaseString) ?? false
            if valueContainsSearchWord {
                self.filteredKeys.append(key as! String)
            }
        }
    
        self.tableView.reloadData()
    }
    

    Fill the tableview with this filtered keys: let key = self.filteredKeys[indexPath.row]

    let dictionary = self.dict[key] as! NSDictionary
    
    cell.cityNameLabel.text = ((dictionary["City"] as? NSDictionary)!["name"] as? NSString)! as String
    
    cell.countryNameLabel.text = ((dictionary["Country"] as? NSDictionary)!["name"] as? NSString)! as String
    
    cell.stateNameLabel.text = ((dictionary["State"] as? NSDictionary)!["name"] as? NSString)! as String
    
    return cell
    

    Just save this filtered dictionary (self.filteredDictionary) and use that to populate the tableView.

    I think the other problem is, when you call the server's search method (getCityNamesFromServer:) from updateSearchResultsForSearchController: the response from the server comes asynchronously and the process afterwards is using the old dictionary data, because the new one is not ready at the time of the processing.

    You should try modifying the getCityNamesFromServer: method with a block completion like this:

    func updateSearchResultsForSearchController(searchController: UISearchController) {
        // Get search word    
        getCityNamesFromServer(searchWord) { () -> Void in
                // Rest of the code comes here
        }
    }
    func getCityNamesFromServer(searchWord:String, completionHandler: (() -> Void) ) {
        let url:String = "http://localhost/"
        let params = ["city":searchWord]
    
        ServerRequest.postToServer(url, params: params) { result, error in
    
            if let result = result {
                print(result)
    
                self.dict = result
    
            }
            completionHandler()
       }
    
    }