Table View Not Reloading from texfield in Parent View - Swift 3

Ok, I am using Algolia Search as an indexing service for my IOS app to Index my Firebase Database and add search functionality. The code they provide in their docs uses a search bar. However, due to the difficulty of customizing the search bar, I decided to see if I could use a text field instead. What I have so far with the text field works successfully. The textfield.text gets passed into the Alogolia Search function and the results are returned successfully and accurately to whatever is typed into the textfield. I am using NSNotification to pass the String - textfiled.text to the child view which is the view of the search results and also where the Algolia Search Function is. I have a picture below that illustrates this.

The Problem: For some reason whenever I use the textfield to search the results get returned in the console but the tableview does not reload. However, whenever I set the search query in the viewDidLoad() the tableView is reloaded and displays the correct results in the cells.

I’ve tried updating the tableview on the Main Thread but that does not seem to work. I believe there is a timing issue with the code, but I’m not completely sure

My Code:

import UIKit
import AlgoliaSearch
import SwiftyJSON
import AFNetworking

class SearchView: UIViewController, UISearchBarDelegate, 
UISearchControllerDelegate, UITableViewDelegate, UITableViewDataSource {


@IBOutlet weak var tableView: UITableView!
var businessSearch = [Business]()
var businessIndex: AlgoliaSearch.Index!
let query = Query()
var searchId = 0
var displayedSearchId = -1
var loadedPage: UInt = 0
var nbPages: UInt = 0
var searchText = String() *** THE VALUE PASSED TO THE SEACRH FUNCTION


var businesses = [Business]()

var searchController: UISearchController! 


override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
                    selector: #selector(SearchView.handleTextChange(_:)),
                    name: NSNotification.Name(rawValue: handleTextChangeNotification),object: nil)


// Search controller
searchController = UISearchController(searchResultsController: nil)
//searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
self.searchController.delegate = self
self.tableView.dataSource = self
self.tableView.delegate = self
// Add the search bar
//tableView.tableHeaderView = self.searchController.searchBar
//definesPresentationContext = true
//searchController.searchBar.sizeToFit()

let apiClient = Client(appID: "APPID", apiKey: "API KEY")
self.businessIndex = apiClient.index(withName: "businessSearch")
query.hitsPerPage = 15
query.attributesToRetrieve = ["businessName","Address","Score","businessType","hours","city"]



 }


*** WHERE TEXTFIELD KEY STROKES ARE OBSERVED AND SAVED
func handleTextChange(_ myNot: Notification) {
if let use = myNot.userInfo {
    if let text = use["text"] {
        searchText = text as! String ***WHERE SEARCH VALUE IS SET
        print(searchText)

        *** WHERE THE SEARCH FUNCTION IS RUN
        query.query = searchText  
        let curSearchId = searchId

        self.businessIndex.search(self.query, completionHandler: {
            (data, error) in

            if curSearchId <= self.displayedSearchId || error != nil {
                print("problem #1")
                print(error?.localizedDescription ?? "Error 1")
                return // Newest query already displayed or error
            }

            // Decode JSON
            guard let hits = data!["hits"] as? [[String: AnyObject]] else { return }
            guard let nbPages = data!["nbPages"] as? UInt else { return }
            self.nbPages = nbPages
            print(hits)
            var tmp = [Business]()
            for hit in hits {
                tmp.append(Business(json: hit))
            }

            //Reload view with the new data
            //self.businessSearch = tmp

            DispatchQueue.main.async(execute: { () -> Void in
                self.businessSearch = tmp
                self.tableView.reloadData()

            })


        })
        self.searchId += 1

    }

}


}

func loadMore() {
if loadedPage + 1 >= nbPages {
    return // All pages already loaded
}
let nextQuery = Query(copy: query)
nextQuery.page = loadedPage + 1
businessIndex.search(nextQuery, completionHandler: {
    (data , error) in

    if nextQuery.query != self.query.query || error != nil {
        print("problem #2")
        return // Query has changed
    }

    self.loadedPage = nextQuery.page! as UInt
    let json = JSON(data!)
    let hits: [JSON] = json["hits"].arrayValue
    var tmp = [Business]()
    print("...\(hits.count)")
    for record in hits {
        tmp.append(Business(json: record.dictionaryObject! as [String : AnyObject]))
    }
    // Display the new loaded page
    self.businessSearch.append(contentsOf: tmp)
    self.tableView.reloadData()
})
}

 *** TABLE VIEW SET UP
 func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return businessSearch.count
}


 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for:indexPath as IndexPath) as! 
SearchCell
let cellData = self.businessSearch[indexPath.row]

cell.nameLabel.text = cellData.businessName
cell.addressLabel.text = "1234 Main St."
cell.hoursLabel.text = "9:00 AM - 10:00 PM"
cell.reviewCountLabel.text = "45 Reviews"
cell.backgroundImage.image = #imageLiteral(resourceName: "samplebg")



if (indexPath.row + 5) >= businessSearch.count {
    loadMore()
}

return cell 
}


deinit{
self.searchController = nil
}

I also asked this question on Stack Overflow here

Let’s handle it through SO :slight_smile:

Found the solution. Posted it here

This would be great to include in your docs so people can have an extremely customizable solution for their apps.

1 Like