Reputation: 11
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
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
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
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) }
}
Upvotes: 0