Storesearch - Crashes if you search same thing twice in a row

This is a bug in the book that I only recently managed to track down.

It happens because the second search returns really fast (using cached results_, before the table view has had a chance to show the activity indicator. Then in Search.swift the state gets changed to .notSearchedYet and – if you’re unlucky – at that moment UIKit will also reload the table view in an attempt to show the activity indicator. But now state is .notSearchedYet and the app crashes on that "Should never get here" line.

This is a typical example of a “race condition” bug that can happen when you’re using multiple threads (or Grand Central Dispatch queues in this case).

The fix is to only change state in the main thread. The changes to Search.swift look like this (indicated by // XXX):

      dataTask = session.dataTask(with: url, completionHandler: {
        data, response, error in
        
        var newState = State.notSearchedYet   // XXX
        var success = false

        if let error = error as? NSError, error.code == -999 {
          // XXX (this bit may actually not matter since we only cancel to
          // start a new serarch anyway)
          DispatchQueue.main.sync {
            self.state = .notSearchedYet   // XXX
          }
          return   // Search was cancelled
        }
        
        if let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200,
          let jsonData = data,
          let jsonDictionary = self.parse(json: jsonData) {
          
          var searchResults = self.parse(dictionary: jsonDictionary)
          if searchResults.isEmpty {
            newState = .noResults   // XXX
          } else {
            searchResults.sort(by: <)
            newState = .results(searchResults)  // XXX
          }
          success = true
        }

        DispatchQueue.main.async {
          self.state = newState                  // XXX
          UIApplication.shared.isNetworkActivityIndicatorVisible = false
          completion(success)
        }
      })

If you make these changes, the code should work OK. :smile: