Improve SearchBar

So I have a searchBar that works when you type in part of the name of a player or players and filters them but when you delete a character or more in the searchbar it does not show anything, I need help trying to add that as a condition in my func.

Code Block
 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if searchText == "" {
      parseJson()
    } else if searchText != "" {
      cPlayerArr = cPlayerArr.filter({ (player) -> Bool in return
        player.yahooName.lowercased().contains(searchText.lowercased())
      })
    }
    collections.reloadData()
  }

Answered by ZoneX in 654702022
Alright I figured it out finally I had to make another array to filter my JSON data.
If you confirm it works when you type one or more char, from the code here, no reason it doesn't work when you backspace.

Problem is likely elsewhere.
What is the result of parseJson ? Does it do some filtering on the array?

Could you show the complete code for the class, specially parseJson ?

Could you test also with some print:
Code Block
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == "" {
parseJson()
} else {
print("searchText", searchText)
cPlayerArr = cPlayerArr.filter({ (player) -> Bool in return
player.yahooName.lowercased().contains(searchText.lowercased())
})
print("cPlayerArr", cPlayerArr)
}
collections.reloadData()
}

Note : retesting on line 4 for "" is not needed.
This is what I have for my class CurrentPlayers.

Code Block
CurrentPlayers {
  var photoUrl: String
  var position: String
  var team: String
  var yahooName: String
  var birthCity: String
  var status: String
   
  init(photoUrl: String, position: String, team: String, yahooName: String, birthCity: String, status: String) {
     
    self.yahooName = yahooName
    self.photoUrl = photoUrl
    self.position = position
    self.team = team
    self.birthCity = birthCity
    self.status = status
     
  }
}

My parseJson func performs a get request for an API and it populates the array with JSON data.

Code Block
func parseJson() {
    cPlayerArr = []
    let url = //url I am using to get data from
    var request = URLRequest(url: URL(string: url)!)
    request.httpMethod = "GET"
     
    let configuration = URLSessionConfiguration.default
    let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
    let task = session.dataTask(with: request) { (data, response, error) in
      if (error != nil) {
        print("error")
      } else {
        do {
          let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
          for eachPlayerNow in fetchedData {
            let eachPlayer = eachPlayerNow as! [String: Any]
            let player = eachPlayer["YahooName"] as?
            String
            let position = eachPlayer["Position"] as? String
            let photoUrl = eachPlayer["PhotoUrl"] as? String
            let team = eachPlayer["Team"] as?
            String
            let birthCity = eachPlayer["BirthCity"] as? String
            let status = eachPlayer["Status"]
            as? String
            self.cPlayerArr.append(CurrentPlayers.init(photoUrl: photoUrl ?? "", position: position ?? "", team: team ?? "", yahooName: player ?? "", birthCity: birthCity ?? "", status: status ?? ""))
          }
          self.collections.reloadData().
        }
        catch {
          print("error 2")
        }
      }
    }
    task.resume()
  }

When you insert 2 chars in search then backspace, what do you get from the print I proposed to insert ?

My guess:
  • when you start searching, you call parseJson()

  • this initialise cPlayerArr

  • when you add a char, you filter cPlayerArr

  • but you never reset it, so when you backspace, you sub filter the filtered array

But you should at least keep the existing filtered elements.
hence, I would need to see the result of the prints, at each iteration.

So could you print as well, in func searchBar
Code Block
        print("search for: ", searchText, searchText.count)

To check if the backspace is interpreted as some additional char (maybe you have something specific in a textField delegate func ?)

Another test would be to redo a parse (just for test):
Code Block
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if searchText == "" {
      parseJson()
    } else {
      parseJson() // JUST To reset cPlayerArr and see if it changes anything
      cPlayerArr = cPlayerArr.filter({ (player) -> Bool in return
        player.yahooName.lowercased().contains(searchText.lowercased())
      })
    }
    collections.reloadData()
  }


Could you also post
  • all searchBar related func

  • the itemAtRow func for the collectionView ?

for my print("search for: ", searchText, searchText.count) it shows the exact amount of characters and the word in the searchbar even when I delete characters. I added the parseJson() right before the filter and it paused it like you said and reset the array. I don't have a textField delegate. This is my cellforItemAt I think thats what you wanted because there is no itemAtRow.

Code Block
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
     
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "colcell", for: indexPath) as! PlayerColCell
     
    let item = cPlayerArr[indexPath.row]
    cell.layer.borderColor = UIColor.black.cgColor
    cell.layer.borderWidth = 0.5
    cell.contentView.backgroundColor = UIColor(red: 102/256, green: 255/256, blue: 255/256, alpha: 0.66)
     
    cell.update(with: item)
     
    return cell
    
  }

for my print("search for: ", searchText, searchText.count) it shows the exact amount of characters and the word in the searchbar even when I delete characters.

So, if you type end in search, you get
end 3

after backspace
en 2

Correct ?

If so, that means there is no issue with the search string.

I added the parseJson() right before the filter and it paused it like you said and reset the array.

Is the array filtered or you get the complete array, even though search is not empty (en in the example above).

If you get the complete array, that means problem is elsewhere.

Could you show the numberOfItemsInSection dataSource func ?
And add a print inside

Code Block
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("Number of cells", cPlayerArr.count)
return cPlayerArr.count // I assume
}

It's not the search string and the array is complete and when parseJson() is run it shows all the cells. When I type E it shows less cells when I type n it shows even less cells, when I type f it shows even less cells but when I delete the f it shows 0 cells and when I delete n it again shows 0 cells so I have to delete everything to restart everything.

This function shows the number of cells I have displayed.

Code Block
 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
     
    print("Number of cells:", cPlayerArr.count)
    return cPlayerArr.count
    
  }

when I delete the f it shows 0 cells 

That is the point I do not understand. Does your collection view hides all the cells with this operation?
As far as I checked your code currently shown, it should continue to show the same cells when you typed f.
Accepted Answer
Alright I figured it out finally I had to make another array to filter my JSON data.

Alright I figured it out finally I had to make another array to filter my JSON data.

Have 2 arrays makes it sure you start filtering from a complete array.
But resetting by doing a parseJson() call each time should have produced a similar effect.
So maybe you have another problem somewhere…
Or you changed something else when adding the second array.
Improve SearchBar
 
 
Q