QuantumHoneybees QuantumHoneybees - 2 years ago 76
Swift Question

TableViewController search results don't load properly because of a lag

So I have an app that pulls up movie results when I type in a search. (Like IMDb.) I use a free API from themoviedb.org to load the results. I load them in a TableViewController. I load the posters for the results using a mod on the .dataTaskWithRequest method. to make it synchronous. Other than that, it's just basic API sending and receiving for the titles, genres, and years of the movies or TV Shows.

Now my app lags when I type too fast, this isn't completely because of the synchronous loading, because it still happens when I don't load images at all, but image loading makes the app lag, too. Now this is an issue in and of itself, but the problem is that when the app loads the words on to the screen, and is done with the lag, the results are the results of part of the word I have on screen. For example, if I type "The Simpsons" too fast, I get results for "The Sim", but if I backspace once, and retype "The Simpsons", the results reload correctly. Something that complicates things even more, is that sometimes I get the top result only being one of the old, partial results, and the rest are normal and loaded underneath.

Here is a video explaining the situation. The first time i type down "the simpsons", you can see the lag. I typed it all really fast, but it lags past the word "the". When it is done loading, it loads up a beowulf result that shouldn't even be there. I have no idea what's going on and it's driving me nuts. Even when I don't load images, and the typing doesn't lag, the results still don't update.

Here are the relevant code snippets, if you want any more, feel free to ask. I just don't want to bombard you with too much code at once:

This updates search results when text is typed in search bar:

extension SearchTable : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {

//To Handle nils
var searchBarText = searchController.searchBar.text
if (searchBarText == nil) {
searchBarText = ""

searchBarText! = searchBarText!.condenseWhitespace()

//To Handle Disallowed Characters
searchBarText = searchBarText!.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())

//Find Results from themoviedb
let urlString = "https://api.themoviedb.org/3/search/multi?query=" + searchBarText! + "&api_key= (I can't post the api key publicly online, sorry)"

let results = NSURL(string: urlString)
if (results == nil) {
//Server Error

//Wire Up Results with matchingItems Array
let task = NSURLSession.sharedSession().dataTaskWithURL(results!) { (data, response, error) -> Void in
if let jsonData = data {

do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)

if var results = jsonData["results"] as? [NSDictionary] {
if results.count > 0 {
//Clean out non-english results:
//I wrote the function, it shouldn't be the source of the lag, but I can still provide it.
self.matchingItems = results
} else {
self.matchingItems = []
} catch {
//JSON Serialization Error



Then, after I get the results, I reload the table using the two required methods from a TableViewDataSource:

//Table Data Source
extension SearchTable {

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count

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

let cell = tableView.dequeueReusableCellWithIdentifier("cell")! as! CustomCell

//Safe-Guard. This shouldn't be needed if I understood what I was doing
if (indexPath.row < matchingItems.count) {
cell.entry = matchingItems[indexPath.row] //404

//Name & Type & Year
//This is only for TV Shows, I removed the rest for simplicity
cell.title.text = matchingItems[indexPath.row]["name"] as? String
cell.icon.image = UIImage(named: "tv.png")

let date = (matchingItems[indexPath.row]["first_air_date"] as? String)
cell.year.text = date == nil ? "" : "(" + date!.substringToIndex(date!.startIndex.advancedBy(4)) + ")"

//Code here removed for simplicity

cell.poster.image = UIImage(named: "Placeholder.jpg")
if let imagePath = matchingItems[indexPath.row]["poster_path"] as? String {
let url = NSURL(string: "http://image.tmdb.org/t/p/w185" + imagePath)
let urlRequest = NSURLRequest(URL: url!)
let session = NSURLSession.sharedSession()
//Synchronous Request
let semaphore = dispatch_semaphore_create(0)
let task = session.dataTaskWithRequest(urlRequest) { data, response, error in
if let poster = UIImage(data: data!) {
cell.poster.image = poster
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

return cell



Answer Source

First of all, I strongly recommend you don't use synchronous request, mainly because it blocks your UI until it finish and this is a very bad responsiveness for an app.

In your case you can place a placeholder for the UIImage and when the request finish substitute it for the correct image.

Regarding your issue of typing faster, it's called throttle or debounce, Apple recommends:

Performance issues. If search operations can be carried out very rapidly, it is possible to update the search results as the user is typing by implementing the searchBar:textDidChange: method on the delegate object. However, if a search operation takes more time, you should wait until the user taps the Search button before beginning the search in the searchBarSearchButtonClicked: method. Always perform search operations a background thread to avoid blocking the main thread. This keeps your app responsive to the user while the search is running and provides a better user experience.

But if you until want it to handle yourself you can see this two good answers explaining how to handle it correctly:

I recommend you handle it as Apple recommends or you can change your philosophy and adopt some libraries that handle it for your automatically like:

The first one in more easy to learn, the second one needs to learn Reactive Programming and concepts of Functional Programming, It's up to you.

I hope this help you.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download