swift activityIndicater is not remove after completing data loading - json

I have a infinite-scrolling in tableView like facebook, it works very well but the problem is activityIndicator does not hide when complete data loading.
NetworkRequestAPI Request
import Foundation
class NetworkRequestAPI {
static func getPropductListByCategory(productId : Int, pageNo : Int , completion: #escaping ([Product]? , Error?) -> ()){
let url = URL(string: Configuration.BASE_URL+"/product-by/type?product_type_id="+String(productId)+"&page="+String(pageNo))
var categoryObject = [Product]()
URLSession.shared.dataTask(with:url!) { (urlContent, response, error) in
if error != nil {
}
else {
do {
let json = try JSONSerialization.jsonObject(with: urlContent!) as! [String:Any]
let products = json["products"] as? [String: Any]
// productCount = (json["product_count"] as? Int)!
let items = products?["data"] as? [[String:Any]]
items?.forEach { item in
let oProduct = Product()
oProduct.product_id = item["product_id"] as? Int
oProduct.product_name = item["product_name"] as? String
oProduct.product_image = item["product_image"] as? String
let ratingItem = item["rating_info"] as? [String: AnyObject]
let rating = RatingInfo()
rating.final_rating = ratingItem?["final_rating"] as? String
oProduct.rating_info = rating
categoryObject.append(oProduct)
}
completion(categoryObject, nil)
} catch let error as NSError {
print(error)
completion(nil, error)
}
}
}.resume()
}
}
ListTableView Class
class ListTableView: UITableViewController {
var isInitUILoad = true
var arrProduct = [[Product]]()
var product_id:Int = 0
var pageNo = 1
override func viewDidLoad() {
super.viewDidLoad()
self.initUILoad()
}
func initUILoad(){
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: true)
NetworkRequestAPI.getPropductListByCategory(productId: product_id, pageNo: pageNo) { (products, error) in
DispatchQueue.main.async(execute: {
if products != nil {
self.pageNo += 1
self.arrProduct.append(products!)
print(self.arrProduct.count)
}else{
print(error.debugDescription)
}
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: false)
self.tableView?.reloadData()
self.isInitUILoad = false
})
}
}
func loadMore(complition:#escaping (Bool) -> ()) {
NetworkRequestAPI.getPropductListByCategory(productId: product_id, pageNo: pageNo) { (products, error) in
DispatchQueue.main.async(execute: {
if error != nil{
print(error.debugDescription)
}
if products != nil && products?.count ?? 0 > 0{
self.pageNo += 1
self.arrProduct.append(products!) self.tableView?.insertSections([self.arrProduct.count - 1], with: .fade)
}else{
print("no product left")
}
complition(true)
})
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: categoryCellid, for: indexPath) as! ListTableCell
var categoryObject = arrProduct[indexPath.section]
cell.product = categoryObject[indexPath.item]
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 115.0
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: footerCellid) as! HeadFooterCell
if !isInitUILoad {
tableView.tableFooterView?.isHidden = true
loadMore(complition: { (isDone) in
footerView.activityIndicatorView.stopAnimating()
})
}
return footerView
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.01
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let lastSectionIndex = tableView.numberOfSections - 1
let lastRowIndex = tableView.numberOfRows(inSection: lastSectionIndex) - 1
if indexPath.section == lastSectionIndex && indexPath.row == lastRowIndex {
// print("this is the last cell")
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
spinner.startAnimating()
spinner.backgroundColor = .green
spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))
self.tableView.tableFooterView = spinner
self.tableView.tableFooterView?.isHidden = false
}
}
HeadFooterCell
class HeadFooterCell: UITableViewHeaderFooterView {
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
setupViews()
}
var activityIndicatorView : UIActivityIndicatorView = {
var activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicatorView.backgroundColor = UIColor.cyan
return activityIndicatorView
}()
func setupViews() {
addSubview(activityIndicatorView)
addConstraintsWithFormat("H:|[v0]|", views: activityIndicatorView)
addConstraintsWithFormat("V:|[v0(50)]|", views: activityIndicatorView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

You need to stop it in the main thread:
DispatchQueue.main.async {
footerView.activityIndicatorView.stopAnimating()
}
Also, set hidesWhenStopped to true:
footerView.activityIndicatorView.hidesWhenStopped = true

Related

Swift JSON Parsing problem, cannot return proper number of cells

I faced a problem as I cannot return number of cells from the fetchedData object.
I can print an array with data however I cannot fill out cells.
It'd be great if anybody could help me resolve this mystery :)
Here's my Model:
import Foundation
struct ExchangeRateModel: Codable {
let table: String
let no: String
let effectiveDate: String
let rates: [Rate]
}
struct Rate: Codable {
let currency: String
let code: String
let mid: Double
}
and rootVC
import UIKit
class RootViewController: UIViewController {
private let cellID = "cell"
private let tabelType = ["a","b","c"]
private let exchangeTabels = ["Tabela A", "Tabela B", "Tabela C"]
private var currentTable = "a"
private let urlString = "https://api.nbp.pl/api/exchangerates/tables/"
var fetchedData = [ExchangeRateModel]()
private let tableView: UITableView = {
let tabel = UITableView()
return tabel
}()
override func viewDidLoad() {
super.viewDidLoad()
configureNavBar()
configureView()
configureTable()
performeRequest()
tableView.reloadData()
}
private func configureNavBar(){
title = "KURSY WALUT NBP"
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Tabele", style: .plain, target: self, action: #selector(tabeleTapped))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshTapped))
}
#objc private func tabeleTapped(){
let ac = UIAlertController(title: "Zmień tabele kursów", message: nil, preferredStyle: .actionSheet)
for table in exchangeTabels {
ac.addAction(UIAlertAction(title: table, style: .default, handler: changeTable))
}
present(ac, animated: true)
}
func changeTable(action: UIAlertAction){
if action.title == exchangeTabels[0]{
currentTable = tabelType[0]
tableView.reloadData()
} else if action.title == exchangeTabels[1] {
currentTable = tabelType[1]
tableView.reloadData()
} else {
currentTable = tabelType[2]
tableView.reloadData()
}
}
#objc private func refreshTapped(){
tableView.reloadData()
}
private func configureView(){
view.backgroundColor = .white
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
private func configureTable(){
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 50
tableView.register(TabelTableViewCell.self, forCellReuseIdentifier: cellID)
}
private func performeRequest(){
let urlString = "\(self.urlString)\(currentTable)"
let url = URL(string: urlString)
print("URL: \(url!)")
guard url != nil else { return }
let session = URLSession(configuration: .default)
let dataTask = session.dataTask(with: url!) { (data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([ExchangeRateModel].self, from: data!)
self.fetchedData = decodedData
print(self.fetchedData)
} catch {
print(error)
}
}
}
DispatchQueue.main.async {
dataTask.resume()
self.tableView.reloadData()
}
}
}
extension RootViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(fetchedData.count)
return fetchedData.count
}
Here I'm not sure how to return those data
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TabelTableViewCell
cell.currencyNameLabel.text = fetchedData[indexPath.row].rates[indexPath.row].currency
cell.currencyCodeLabel.text = fetchedData[indexPath.row].rates[indexPath.row].code
cell.effectiveDateLabel.text = fetchedData[indexPath.row].effectiveDate
cell.midRateLabel.text = String(fetchedData[indexPath.row].rates[indexPath.row].mid)
return cell
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let nextVC = CurrencyViewController()
navigationController?.pushViewController(nextVC, animated: true)
}
}
Thank you all in advance!
Need to reload tableview after getting the value from server.
Try this code
private func performeRequest(){
let urlString = "\(self.urlString)\(currentTable)"
let url = URL(string: urlString)
print("URL: \(url!)")
guard url != nil else { return }
let session = URLSession(configuration: .default)
let dataTask = session.dataTask(with: url!) { (data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([ExchangeRateModel].self, from: data!)
self.fetchedData = decodedData
print(self.fetchedData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
dataTask.resume()
}
The json contains only one ExchangeRateModel in the array.
Seems like you want to show the rates. You need to return fetchedData[0].rates.count instead of return fetchedData.count
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if fetchedData.count > 0 {
return fetchedData[0].rates.count
}
return 0
}
Also you need to update cellForRowAtIndexPath method like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TabelTableViewCell
cell.currencyNameLabel.text = fetchedData[0].rates[indexPath.row].currency
cell.currencyCodeLabel.text = fetchedData[0].rates[indexPath.row].code
cell.effectiveDateLabel.text = fetchedData[0].effectiveDate
cell.midRateLabel.text = String(fetchedData[0].rates[indexPath.row].mid)
return cell
}
You did't reload table after coming response from the api call. inside response callback where you print the array of response you have to reload tableview.
do {
let decodedData = try decoder.decode([ExchangeRateModel].self, from: data!)
self.fetchedData = decodedData
print(self.fetchedData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
I also added DispatchQueue and updated all other places.
See below:
private func performeRequest(){
let urlString = "\(self.urlString)\(currentTable)"
let url = URL(string: urlString)
print("URL: \(url!)")
guard url != nil else { return }
let session = URLSession(configuration: .default)
let dataTask = session.dataTask(with: url!) { (data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([ExchangeRateModel].self, from: data!)
self.fetchedData = decodedData
print(self.fetchedData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
dataTask.resume()
}
}
and also table:
extension RootViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(fetchedData.count)
return fetchedData[0].rates.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! TabelTableViewCell
cell.currencyNameLabel.text = fetchedData[0].rates[indexPath.row].currency
cell.currencyCodeLabel.text = fetchedData[0].rates[indexPath.row].code
cell.effectiveDateLabel.text = fetchedData[0].effectiveDate
cell.midRateLabel.text = String(fetchedData[0].rates[indexPath.row].mid)
return cell
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let nextVC = CurrencyViewController()
navigationController?.pushViewController(nextVC, animated: true)
}
}
and I'm still getting this error.

Issue adding sections to tableview from JSON data

I am trying to group my table view that is being populated from JSON data.
Here is an example of what it looks like:
[{"customer":"Customer1","serial":"34543453",
"rma":"P2384787","model":"M282","manufacturer":"Manufacturer1"},
{"customer":"Customer1","serial":"13213214",
"rma":"P2384787","model":"M384","manufacturer":" Manufacturer1"},
{"customer":"Customer2","serial":"1212121323",
"rma":"P3324787","model":"M384","manufacturer":" Manufacturer1"}]
I would like to group the table view based on the customer name.
So in my case, it should look like:
Customer1
34543453 - Manufacturer1 - M282
13213214 - Manufacturer1 - M384
Customer2
1212121323 - Manufacturer1 - M384
NOTE:
The reason there is a line separating the serial manufacturer and model is because of this separator in CustomerViewController.swift:
let titleStr = [item.serial, item.manufacturer, item.model].compactMap { $0 }.joined(separator: " - ")
PortfolioController.swift
import UIKit
class PortfolioController: UITableViewController {
var portfolios = [Portfolios]()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Customer"
fetchJSON()
}
func fetchJSON(){
let urlString = "https://www.example.com/example/example.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, error) in
DispatchQueue.main.async {
if let error = error {
print("Failed to fetch data from url", error)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.portfolios = try decoder.decode([Portfolios].self, from: data)
self.tableView.reloadData()
} catch let jsonError {
print("Failed to decode json", jsonError)
}
}
}.resume()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return portfolios.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let customer = portfolios[indexPath.row]
//cell.textLabel?.text = customer.serial
let titleStr = [customer.serial, customer.manufacturer, customer.model].compactMap { $0 }.joined(separator: " - ")
print(titleStr)
// Get references to labels of cell
cell.textLabel!.text = titleStr
return cell
}
}
Portfolios.swift
import UIKit
struct Portfolios: Codable {
let customer, serial, rma, model: String
let manufacturer: String
}
1- Create an instance var
var portfoliosDic = [String:[Portfolios]]()
2- Assign it here
let res = try JSONDecoder().decode([Portfolios].self, from: data)
self.portfoliosDic = Dictionary(grouping: res, by: { $0.customer})
DispatchQueue.main.async {
self.tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return portfoliosDic.keys.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let keys = Array(portfoliosDic.keys)
let item = portfoliosDic[keys[section]]!
return item.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let keys = Array(portfoliosDic.keys)
let arr = portfoliosDic[keys[indexPath.section]]!
let customer = arr[indexPath.row]
//cell.textLabel?.text = customer.serial
let titleStr = [customer.serial, customer.manufacturer, customer.model].compactMap { $0 }.joined(separator: " - ")
print(titleStr)
// Get references to labels of cell
cell.textLabel!.text = titleStr
return cell
}

Search UITableView cells using UISearchBar text

I want to show filtered cells in UITableView using UISearchBar, but it's not working. My search function is at the end of my code.
import UIKit
struct User2: Codable {
let firstName: String
let lastName: String
let email: String
let userid: String
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case email = "email"
case userid = "user_id"
}
}
class SearchViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
#IBOutlet var searchBtn: UISearchBar!
private var dataSource = [User]() {
didSet {
self.tableview.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableview.register(UITableViewCell.self, forCellReuseIdentifier: "groupCell")
self.tableview.dataSource = self
self.tableview.delegate = self
let url = URL(string: "https://ex.com/ex.php")
URLSession.shared.dataTask(with: url!, completionHandler: { [weak self] (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "An error occurred")
return
}
DispatchQueue.main.async {
self?.dataSource = try! JSONDecoder().decode([User].self, from: data)
}
}).resume()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableview.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "groupCell", for: indexPath)
let user = self.dataSource[indexPath.row]
cell.textLabel?.text = user.firstName + " " + user.lastName
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let user = self.dataSource[indexPath.row]
let userVC = DataViewController(user: user)
self.navigationController?.pushViewController(userVC, animated: true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
User2 = User2.filter({ (item) -> Bool in
let heading: NSString = ((item["firstName"]! as? String)! + (item["lastName"]! as! String)) as NSString
return (heading.range(of: searchText, options: NSString.CompareOptions.caseInsensitive).location) != NSNotFound
})
self.tableview.reloadData()
}
}
A better approach would be to have 2 dataSources, one for normal state, another when there is active search, like so:
class SearchViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private var dataSource = [User]() {
didSet {
self.tableview.reloadData()
}
}
private var searchDataSource: [User]? {
didSet {
self.tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
...
self.searchBtn.delegate = self
}
// some changes to UITableView DataSource/Delegate
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.searchDataSource?.count ?? self.dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
let user = self.searchDataSource?[indexPath.row] ?? self.dataSource[indexPath.row]
...
}
}
Finally use following extension extending UISearchBarDelegate
extension SearchViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
self.searchDataSource = nil
} else {
self.searchDataSource = self.dataSource.filter({
let fullName = $0.firstName + " " + $0.lastName
return fullName.range(of: searchText, options: [.anchored, .caseInsensitive]) != nil
})
}
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
if self.searchDataSource != nil && searchBar.text?.isEmpty ?? true {
self.searchDataSource = nil
}
}
}
that yields
I think filter should be applied on dataSource as, where dataSource is of kind [User2]:
let filteredArr: [User] = dataSource.filter({ $0.firstName.lowercased().contains(searchText.lowercased()) || $0.lastName.lowercased().contains(searchText.lowercased()) })
You can try to apply filtering in your dataSource like:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let filteredArray = dataSource.filter({ user in
let username = "\(user.firstName) \(user.lastName)"
return username.range(of: searchText, options: .caseInsensitive) != nil
})
self.tableview.reloadData()
}

Trouble With SearchBar and Search Bar Controller Due to Depreciation of SearchDisplayController

I would like some help with the search bar functionality. I am stuck and not sure where to take it from here. I am trying to update the tableview when search text word is contained in a recipe title. I am not sure how to do this because of the depreciated searchDisplay controller. Help would be appreciated.
import UIKit
import SwiftyJSON
class Downloader {
class func downloadImageWithURL(_ url:String) -> UIImage! {
let data = try? Data(contentsOf: URL(string: url)!)
return UIImage(data: data!)
}
}
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate,UISearchDisplayDelegate{
#IBOutlet weak var recipeTable: UITableView!
// search functionality Need help with my search functionality
var filteredRecipes = [Recipe]()
func filterContentForSearch(searchText:String) {
// need help here
self.filteredRecipes = self.recipes.filter({(title:Recipe) -> Bool in
return (title.title!.lowercased().range(of: searchText.lowercased()) != nil)
})
}
private func searchDisplayController(controller: UISearchController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
self.filterContentForSearch(searchText: searchString)
return true
}
//end search parameters
// tableview functionionalitys
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == searchDisplayController!.searchResultsTableView {
return filteredRecipes.count
}else{
return recipes.count
}
// recipeTable.reloadData()
}
// tableview functionalities
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RecipeTableViewCell
if tableView == self.searchDisplayController!.searchResultsTableView{
//get images from download
DispatchQueue.main.async { () ->Void in
cell.imageLabel.image = Downloader.downloadImageWithURL(self.filteredRecipes[indexPath.row].imageUrl)
}
cell.recipeLabel.text = self.filteredRecipes[indexPath.row].title
recipeTable.reloadData()
}else{
//get image from download
DispatchQueue.main.async { () ->Void in
cell.imageLabel.image = Downloader.downloadImageWithURL(self.recipes[indexPath.row].imageUrl)
}
cell.recipeLabel.text = recipes[indexPath.row].title
}
//recipeTable.reloadData()
return cell
}
// structs for json
struct Root : Decodable {
let count : Int
let recipes : [Recipe]
}
struct Recipe : Decodable { // It's highly recommended to declare Recipe in singular form
let recipeId : String
let imageUrl, sourceUrl, f2fUrl : String
let title : String?
let publisher : String
let socialRank : Double
let page : Int?
let ingredients : [String]?
}
//recipes is array of Recipes
var recipes = [Recipe]() // array of recipes
//unfiltered recipes to put into search
fileprivate func getRecipes() {
let jsonURL = "https://www.food2fork.com/api/search?key=264045e3ff7b84ee346eb20e1642d9d9"
//.data(using: .utf8)!
guard let url = URL(string: jsonURL) else{return}
URLSession.shared.dataTask(with: url) {(data, response , err) in
if let response = response as? HTTPURLResponse, response.statusCode != 200 {
print(response.statusCode)
return
}
DispatchQueue.main.async {
if let err = err{
print("failed to get data from URL",err)
return
}
guard let data = data else{return}
//print(String(data: data, encoding: .utf8))
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Root.self, from: data)
self.recipes = result.recipes
//print(result.recipes)
self.recipeTable.reloadData()
}catch let jsonERR {
print("Failed to decode",jsonERR)
}
}
}.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
//recipeTable.reloadData()
//search bar
//filteredRecipes = recipes
//call json object
getRecipes()
}
}
You could take this approach:
Add a Boolean variable to indicate whether searching or not
var searching: Bool = false
Use this for numberOfRowsInSection for the tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return filteredRecipes.count
} else {
return recipes.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RecipeTableViewCell
var recipe: Recipe
if searching {
recipe = filteredRecipes[indexPath.row]
} else {
recipe = recipes[indexPath.row]
}
DispatchQueue.main.async { () ->Void in
cell.imageLabel.image = Downloader.downloadImageWithURL(recipe.imageUrl)
}
cell.recipeLabel.text = recipe.title
return cell
}
And add this for the searchBar (set searching for other funcs like searchBarCancelButtonClicked)
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
searching = false
filteredRecipes.removeAll()
view.endEditing(true)
} else {
searching = true
filteredRecipes = recipes.filter{$0.title.contains(searchBar.text!)}
}
tableView.reloadData()
}

Hi there , I'm new to iOS development and I'm having a hard time populating a tableview embedded in a UIviewcontroller with json data

I'm new to iOS development and I'm having a hard time populating a tableview embedded in a UIviewcontroller with json data.
''import UIKit
class
FirstViewController:UIViewController,UITableViewDataSource,UITableViewDelegate{
#IBOutlet weak var tableview: UITableView!
var TableData:Array< String > = Array < String >()
var valueToPass:String!
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.kaleidosblog.com/tutorial/tutorial.json")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FirstViewCell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let countries_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let country_obj = countries_list[i] as? NSDictionary
{
if let country_name = country_obj["country"] as? String
{
if let country_code = country_obj["code"] as? String
{
TableData.append(country_name + " [" + country_code + "]")
}
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
func do_table_refresh()
{
tableview.reloadData()
}
}
you need to set tableView's dataSource and delegate to self.