Uvesh Imam
Uvesh Imam

Reputation: 11

How to implement UISearchBar to filter name or capital JSON using JSON Decoder in swift iOS application

How to implement UISearchBar to filter name or capital JSON using JSON Decoder in swift iOS application. I want to implement UISearchBar and search results or filter results using name from JSON Data.

  import UIKit

Structure Created

  struct jsonstruct:Decodable
  {
  let name:String   
   let capital:String
  }

  class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate, UISearchBarDelegate, UISearchControllerDelegate, UISearchDisplayDelegate {

Creating Outlet for TableView and SearchBar

   @IBOutlet var tableview: UITableView!

   @IBOutlet var searchBar: UISearchBar!

Declaring JSON

   var arrdata = [jsonstruct]()

Function for getting Data

func getdata()
{
let url = URL(string: "https://restcountries.eu/rest/v2/all")

URLSession.shared.dataTask(with: url!)
{
(data, response, error) in

do
{
if error == nil
{
self.arrdata = try
JSONDecoder().decode([jsonstruct].self, from: data!)

for mainarr in self.arrdata
{
print(mainarr.name,":",mainarr.capital as Any)
DispatchQueue.main.async 
{
self.tableview.reloadData()
}
}
}
}
catch
{
print(error.localizedDescription)
}
}.resume()
}

TABLE VIEW

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell:TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.label1.text = "Name: \(arrdata[indexPath.row].name)"
cell.label2.text = "Capital: \(arrdata[indexPath.row].capital)"
return cell
}

OverRiding Function

override func viewDidLoad() 
{
getdata()
}

Upvotes: 0

Views: 341

Answers (3)

naz
naz

Reputation: 1506

Assuming you are not caching all your data and the filtering is done live via an API. You will need to set an object or the viewcontroller as a delegate of the search bar(UISearchBarDelegate). Then use the searchText as the text for your API query.

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    //call throttle that will call urlsession
}

Since one character is typed at a time we do not to call the API every time. You may need to use a throttler to make lesser API calls instead of sending character by character search. You might find this tutorial about throttling helpful: Simple Throttling in Swift .

Most REST APIs should have a filter feature and you could just easily append the typed name or capital.

https://restcountries.eu/rest/v2/name/append name here
https://restcountries.eu/rest/v2/capital/append capital here

This is an example networking code to fetch the results. Use the results to call another method safely on the main queue to reload the tableview.

if let url = URL(string: "https://restcountries.eu/rest/v2/country?q=name") {
   URLSession.shared.dataTask(with: url) { data, response, error in
      if let data = data {
          do {
             let results = try JSONDecoder().decode(YourCustomDecodeStruct.self, from: data)
             //safely your data source and reload the tableview
          } catch let error {
             print(error)
          }
       }
   }.resume()
}

Upvotes: 0

Zain
Zain

Reputation: 153

You need to make two objects of data, one original data and other filtered data.

var filteredArrData = [jsonstruct]()
var arrdata = [jsonstruct]()

Than in your getData functions:

do {
 self.arrdata = try JSONDecoder().decode([jsonstruct].self, from: data!)
 self.filteredArrData = self.arrdata
}

Then in your table view delegate and data source:

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell:TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
   cell.label1.text = "Name: \(filteredArrData[indexPath.row].name)"
   cell.label2.text = "Capital: \(filteredArrData[indexPath.row].capital)"
   return cell
}

Than make filter function like this:

func applyFilters(textSearched: String) {
            filteredArrData = arrdata.filter({ item -> Bool in
            return item.name.lowercased().hasPrefix(textSearched.lowercased())
        })
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }

Then pass your string to this function and everything will work fine.

Upvotes: 1

Abdul Karim Khan
Abdul Karim Khan

Reputation: 4935

Make TextField with an IBAction of didbegin like below and create an array where you can have filtered data.

@IBAction func tfSearch(_ sender: UITextField) {
    let filteredArray = yourArr.filter { $0.contains(sender.text) }
}

enter image description here

Upvotes: 0

Related Questions