How do i fix JSON not populating UITableView? - swift

Please help as I have hit a road b lock with this issue. I have tried these URL1 & URL2 for guidance but haven't been successful.
How can I fix JSON which is not populating my UITableView
Here is my code:
class FilmsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var tableView : UITableView!
var FilmArray = [String]()
let film_url = "https://www.testing.com/api/resources/films/1"
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Getting the right element
//let films = FilmArray[indexPath.row]
// Instantiate a cell
//let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "moviecell")
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FilmsAPITableViewCell
// cell.movieTitle.text = FilmArray[indexPath.row]
// Adding the right informations
cell.movieTitle.text = FilmArray[indexPath.row]
// Returning the cell
return cell
}
// #IBOutlet weak var FilmsView: UITableView!
// weak var tableView : UITableView!
// var FilmArray = [String]()
//
// let film_url = "https://www.distribber.com/api/resources/films/1"
//
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView (frame:view.bounds)
view.addSubview(tableView)
self.tableView = tableView
tableView.dataSource = self
tableView.delegate = self
// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return 1
// }
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// // Getting the right element
// //let films = FilmArray[indexPath.row]
//
//
// // Instantiate a cell
// //let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "moviecell")
// let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! FilmsAPITableViewCell
// // cell.movieTitle.text = FilmArray[indexPath.row]
// // Adding the right informations
// cell.movieTitle.text = FilmArray[indexPath.row]
// // Returning the cell
// return cell
// }
// }
//}
let url:URL = URL(string: film_url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.setValue("masked", forHTTPHeaderField: "X-API-KEY")
request.setValue("masked=", forHTTPHeaderField: "Authorization")
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = ""
// for (key, value) in post_data
// {
// paramString = paramString + (key as! String) + "=" + (value as! String) + "&"
// }
//
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data!, options: [])
// Prasing JSON
var parsedData = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
print(parsedData)
if let FilmArray = parsedData["films"] as? NSArray {
for movieTitle in FilmArray{
if let filmDict = movieTitle as? NSDictionary{
if let film = filmDict.value(forKey: "title") {
self.FilmArray.append(film as! String)
}
//OperationQueue.main.addOperation({
//self.FilmsView.reloadData()
// })
}
}
}
print("Hello")
print(self.FilmArray)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
if let data_block = server_response["data"] as? NSDictionary
{
if let session_data = data_block["session"] as? String
{
// self.login_session = session_data
let preferences = UserDefaults.standard
preferences.set(session_data, forKey: "session")
// DispatchQueue.main.async(execute: self.LoginDone)
}
}
})
task.resume()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
["status": 1, "films": <__NSSingleObjectArrayI 0x7ae40320>(
{
Stores = (
{
Assets = (
{
"asset_name" = Screener;
"asset_status" = Needed;
},
{
"asset_name" = Feature;
"asset_status" = Needed;
},
{
"asset_name" = Trailer;
"asset_status" = Needed;
},
{
"asset_name" = Artwork;
"asset_status" = Needed;
},
{
"asset_name" = "Closed Caption";
"asset_status" = Needed;
},
{
"asset_name" = Meta;
"asset_status" = Needed;
}
);
Status = Declined;
"store_name" = "Netflix Watch <br> Instantly (SD)";
}
);
title = "The Red Tail";
}
)
]
Please guide.

Related

How to Increase count of Page in Url for loading more data and show indicator at bottom?

I Creating a demo of webservices, In this I want to increase page count and load more data from api, and add in table view after activity indicator refreshing. I find many tutorials but Not found useful... They are all Advance and I'm beginner so i didn't get properly. Can Any one please tell how to do this.
Here's My Demo details...
This Is Page Count of URL
"info": {
"count": 826,
"pages": 42,
"next": "https://rickandmortyapi.com/api/character/?page=3",
"prev": "https://rickandmortyapi.com/api/character/?page=1"
},
My json Model
import UIKit
import Foundation
// MARK: - JsonModel
struct JSONModel:Decodable {
let info: Info
let results: [Result]
}
// MARK: - Info
struct Info : Decodable {
let count, pages: Int
let next: String
let prev: NSNull
}
// MARK: - Result
struct Result : Decodable {
let id: Int
let name: String
let status: Status
let species: Species
let type: String
let gender: Gender
let origin, location: Location
let image: String
let episode: [String]
let url: String
let created: String
}
enum Gender {
case female
case male
case unknown
}
// MARK: - Location
struct Location {
let name: String
let url: String
}
enum Species {
case alien
case human
}
enum Status {
case alive
case dead
case unknown
}
This is my View controller Class
import UIKit
import Kingfisher
class ViewController: UIViewController,UISearchBarDelegate{
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var results = [Results]()
var filteredData = [Results]()
var batchSize = 42
var fromIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
apiCalling()
filteredData = results
}
override func viewWillAppear(_ animated: Bool) {
filteredData = results
self.tableView.reloadData()
}
func apiCalling(){
guard let url = URL(string: "https://rickandmortyapi.com/api/character/") else { return }
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results = resultData.results!
self?.filteredData = self!.results
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
}.resume()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let searchText = searchBar.text!
guard !searchText.isEmpty else {
filteredData = results
tableView.reloadData()
return
}
filteredData = results.filter({ $0.name!.lowercased().contains(searchText.lowercased() ) })
tableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.searchBar.showsCancelButton = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = false
searchBar.text = ""
searchBar.resignFirstResponder()
filteredData.removeAll()
self.tableView.reloadData()
}
}
This My Tableview Extension
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
let row = filteredData[indexPath.row]
let imageUrl = URL(string: row.image!)
cell.userImage.kf.setImage(with: imageUrl)
cell.lblGender.text = "Gender:- \(row.gender ?? "no value")"
cell.lblID.text = "ID:- \(row.id ?? 0)"
cell.lblName.text = "Name: \(row.name!)"
cell.lblSpecies.text = "Species:- \(row.species ?? "No Speies")"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
}
u need save page info.
self?.info = resultData.info!
call "loadpage" when u loading more data
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
filteredData = []
result = []
apiCalling(apiurl:"https://rickandmortyapi.com/api/character/")
}
func apiCalling(apiurl:String){
guard let url = URL(string: apiurl) else { return }
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results.append(resultData.results!)
self?.info = resultData.info!
filterWord()
} catch {
print(error.localizedDescription)
}
}.resume()
}
func filterWord(){
let searchText = searchBar.text!
guard !searchText.isEmpty else {
filteredData = results
tableView.reloadData()
return
}
filteredData = results.filter({ $0.name!.lowercased().contains(searchText.lowercased() ) })
tableView.reloadData()
}
func loadPage(){
guard let page = self?.info.next,!page.isEmpty else{
return
}
apiCalling(apiurl:page)
}
under indicator simple example like this
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let page = self?.info.next,!page.isEmpty else{
return nil
}
//press to call loadPage
let loading = UIButton.init()
let view = UIView.init()
view.addSubview(loading)
return view
}
I'm Giving My own Questions answer Here...
I Have Create 3 more variables
var curentIndex : Int = 0
// I'm Putting Default Limit Here...
var numberArray = Array(1...42)
var fetchingMore = false
Api Call
func apiCalling(){
guard !fetchingMore else {
print("Didn't call Get Data")
return
}
fetchingMore = true
guard let url = URL( string: "\(baseUrl)?page=\(numberArray[curentIndex])") ?? URL(string: "" ) else {
fetchingMore = false
return
}
curentIndex += 1
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results += resultData.results!
self?.filteredData = self!.results
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
self?.fetchingMore = false
}.resume()
}
**Here's My CellForRowMethod **
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
let row = filteredData[indexPath.row]
if indexPath.row == filteredData.count - 1 && curentIndex <= row.id ?? 0 {
apiCalling()
}
let imageUrl = URL(string: row.image!)
cell.userImage.kf.setImage(with: imageUrl)
cell.lblGender.text = "Gender:- \(row.gender ?? "no value")"
cell.lblID.text = "ID:- \(row.id ?? 0)"
cell.lblName.text = "Name: \(row.name!)"
cell.lblSpecies.text = "Species:- \(row.species ?? "No Speies")"
return cell
}

Reloading the tableview inside a ViewController getting me ' Fatal error: index out of range' , in cellforRow at index path

When I open the view it opens up quickly and doesn't show any errors but when I reopen it, it gives me 'fatal error'. This is a tableview inside the ViewController which is getting me the data but when I reopen the view it gives error.
The tableview is embedded inside the view controller when I call the tableview again it gives fatal error index out of range inside the ViewController. This needs to be solved I think issue is in my number of rows in section returning the array.
import UIKit
import Alamofire
import Foundation
protocol Quantitypass {
func qtypass(qty: String, product_id: String)
}
public struct QtyCart {
var qty : String!
public init(qty: String) {
self.qty = qty
}
}
public struct FavouriteCart {
var p_name : String!
var p_price : String!
var p_id : String!
var qty: String!
public init(p_name: String , p_price: String , p_id : String, qty: String) {
self.p_name = p_name
self.p_price = p_price
self.p_id = p_id
self.qty = qty
}
}
public struct favSection {
var favitems: [FavouriteCart]
public init(favitems: [FavouriteCart] ) {
self.favitems = favitems
}
}
public struct MyVariables {
static var product2 = "p_id"
}
public var cartData: [favSection] = []
class newCartViewController: UIViewController {
#IBAction func btnSubmit(_ sender: UIButton) {
self.loadnextVC()
}
func loadnextVC() {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "finalVC")
self.present(newViewController, animated: true, completion: nil)
}
#IBOutlet weak var lblPreorder: UILabel!
#IBOutlet weak var lblOrdermethod: UILabel!
#IBOutlet weak var lblTotalAmount: UILabel!
// public var itemsData: [Item] = []
var lblOrder = String()
// let productPrice = UserDefaults.standard.object(forKey: "p_price")
let cartid = UserDefaults.standard.object(forKey: "deviceUUID") as? String
let getApi = RestaurantAPI.self
var qtyData: [QtyCart] = []
// var currentSection = 0
// var currentRow = 1
// var calculation1 = cartTableViewCell()
#IBOutlet weak var tableView: UITableView!
#IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
self.lblTotalAmount.text = ""
}
//public var cartData: [FavouriteCart] = []
var refreshControl = UIRefreshControl()
var buttonClicked = statusViewController.self
var btn2 = UserDefaults.standard.object(forKey:"button1") as? Bool
var btn3: Bool = true
weak var delegate: DetailsDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.getTotal1()
print("#------/\(cartData.count)")
//self.updateLabel(withString: lblTotalAmount.text)
if btn2 != btn3 {
lblPreorder.isHidden = true
lblOrdermethod.isHidden = false
print("BUTTONCLICKED")
UserDefaults.standard.removeObject(forKey: "button1")
} else if btn2 == btn3 {
lblPreorder.isHidden = false
lblOrdermethod.isHidden = true
print("BUTTON-NOT-CLICKED")
UserDefaults.standard.removeObject(forKey: "button1")
}
// if #available(iOS 10.0, *) {
// tableView.refreshControl = refreshControl
// } else {
// tableView.addSubview(refreshControl)
//
// }
// self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
// self.refreshControl.addTarget(self, action: #selector(newCartViewController.refreshData), for: UIControlEvents.valueChanged)
//
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.addSubview(self.tableView)
self.tableView.dataSource = self
self.tableView.delegate = self
self.getTotal1()
self.getFav()
self.tableView.reloadData()
// self.updateTableview()
// self.getTotal1()
}
func getFav() {
getFav(completionHandler: { success in
if success {
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
})
}
//TOTAL API CALL:
func getTotal1() {
if cartid != nil {
let request = getApi.getamountcartGetWithRequestBuilder(restId: "17", cartId: cartid!)
Alamofire.request(request.URLString, method: .get , parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
print("123321#######/\(response)")
let res = response
print("101res/\(res)")
if let value = response.value as? [String: AnyObject] {
if let success = value["error"] as? Bool {
if success == false {
// var grandtotal: Any? = value["total"]
if let grandtotal = value["total"] as? Double {
self.lblTotalAmount.text = String(grandtotal)
print("!#/\(String(grandtotal))")
var g = String(grandtotal)
UserDefaults.standard.set(g, forKey: "g")
}
// self.tableView.reloadData()
}
}
}
}
}
}
//
// Mark: getting all cart items:-->
func getFav(completionHandler: #escaping (Bool) -> Void){
if cartid != nil {
let request = getApi.displaycartGetWithRequestBuilder(restId: "17", cartId:cartid!)
Alamofire.request(request.URLString, method: .get , parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
print("123321/\(response)")
let res = response
print("101res/\(res)")
// let total = cartData
// print("cartd1/\(total)")
if let value = response.value as? [String: AnyObject] {
if let success = value["error"] as? Bool {
if success == false {
print("2222/\(response)")
if let response = value["cartdata"] as? [String: AnyObject] {
let cart = response["simple"] as! [[String: AnyObject]]
// let total = value["total"] as! [String: Any]
// print("1231231231234/\(total)")
//let userdata: [Favourites] = []
//
var cartitems: [FavouriteCart] = []
for (_ , value) in cart.enumerated() {
let obj = value
let p_name = obj["p_name"] as! String
let p_price = obj["p_price"] as! String
let p_id = obj["p_id"] as! String
let qty = obj["qty"] as! String
// UserDefaults.standard.set(qty, forKey: "qty")
let item = FavouriteCart(p_name: p_name, p_price: p_price, p_id: p_id, qty: qty)
cartitems.append(item)
DispatchQueue.main.async{
print("RELOADED-----ON-----API")
cartData.append(favSection(favitems: cartitems))
self.tableView.reloadData()
}
}
}
print("COMPLETION-------------HANDLER")
completionHandler(true)
}
}
else
{
let myAlert = UIAlertController(title:"Alert",message:value["error_msg"] as? String,preferredStyle:UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title:"OK",style:UIAlertActionStyle.default , handler: nil)
myAlert.addAction(okAction)
}
}
}
}
}
// Mark:--> Delete items from cart.
// func delFav(p_id: String ,completionHandler: #escaping (Bool) -> Void) {
func delFav(p_id: String, completionHandler: #escaping (Bool) -> Void){
//var product1 = UserDefaults.standard.object(forKey: "p_id")
//print("this is my \(product1)")
let request = getApi.deleteproductcartGetWithRequestBuilder(restId: "17", cartId: cartid!, productId: p_id , freeDish: "none", type: "simple")
Alamofire.request(request.URLString, method: .delete , parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
print("del favvvvvvvvv\(response)")
print("<<DELETED ------------ ITEM>>")
//print(response)
if let value = response.value as? [String: AnyObject] {
if let success = value["error"] as? Bool {
if success == false {
let response = value["cartdata"] as? [String]
print("10001 - > /\(response)")
//self.tableView.reloadData()
} else
{
print("error message")
}
}
}
// completionHandler(true)
}
}
func delterow(p_id: String) {
self.delFav(p_id: p_id, completionHandler: {sucess in
if sucess {
self.tableView.reloadData()
print("DELETED_ITEM")
}
})
}
func updateTotal(withString string: String?) {
lblTotalAmount.text = string
}
}
extension newCartViewController: UITableViewDelegate , UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90;
}
// func numberOfSections(in tableView: UITableView) -> Int {
// return cartData.count
// }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return sectionsData[section].items.count
// return cartData[section].favitems.count
// return cartData[section]..count
return cartData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartTablecell", for: indexPath) as! cartTableViewCell
let item: FavouriteCart = cartData[indexPath.section].favitems[indexPath.row]
cell.btnsub1.tag = indexPath.row
cell.btnadd1.tag = indexPath.row
cell.lblItemName.text = item.p_name
cell.productPrice.text = item.p_price
cell.lblprice.text = item.p_price
cell.lblQuantity.text = item.qty
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool{
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
if cartData.isEmpty{
print("EmptyCart")
}else {
print("0-0/\(cartData.count)")
// let item: Item = sectionsData[indexPath.section].items[indexPath.row]
let item: FavouriteCart = cartData[indexPath.section].favitems[indexPath.row]
self.tableView.beginUpdates()
cartData.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .fade)
self.delterow(p_id: item.p_id)
self.getTotal1()
self.tableView.endUpdates()
}
}
}
}
It was giving the error because the rows were saved at local but I was calling the data again changes are :
have to delete the whole cart when close the view
#IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
self.lblTotalAmount.text = ""
cartData.removeAll()
}
I think self.tableView.dataSource = self & self.tableView.delegate = self should be in viewDidLoad method. Adding it to viewDidLoad might work.

How to update table view

I used search bar. It isn't updating the table view.
struct ApiResults:Decodable {
let resultCount: Int
let results: [Music]
}
struct Music:Decodable {
let trackName: String?
let artistName: String?
let artworkUrl60: String?
}
class ItunesDataViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var musicArray:[Music] = []
var mArray:[Music] = []
var filteredData:[Music] = []
var isSearching = false
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
self.searchBar.delegate = self
searchBar.placeholder = "search"
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
print("\n\nsearchText : \(searchText)\n\n")
Search(searchTerm: "\(searchText)")
if searchBar.text == nil || searchBar.text == ""
{
isSearching = false
view.endEditing(true)
self.tableView.reloadData()
}
else
{
isSearching = true
filteredData = mArray.filter{$0.artistName == searchText}
self.tableView.reloadData()
}
}
func Search(searchTerm: String)
{
guard let url = URL(string: "https://itunes.apple.com/search?term=\(searchTerm)&attribute=actorTerm&attribute=languageTerm&attribute=allArtistTerm&attribute=tvEpisodeTerm&attribute=shortFilmTerm&attribute=directorTerm&attribute=releaseYearTerm&attribute=titleTerm&attribute=featureFilmTerm&attribute=ratingIndex&attribute=keywordsTerm&attribute=descriptionTerm&attribute=authorTerm&attribute=genreIndex&attribute=mixTerm&attribute=allTrackTerm&attribute=artistTerm&attribute=composerTerm&attribute=tvSeasonTerm&attribute=producerTerm&attribute=ratingTerm&attribute=songTerm&attribute=movieArtistTerm&attribute=showTerm&attribute=movieTerm&attribute=albumTerm") else {return}
URLSession.shared.dataTask(with: url){(data, response, error) in
guard let data = data else {return}
do
{
let apiressults = try JSONDecoder().decode(ApiResults.self, from: data)
for item in apiressults.results
{
if let track_Name = item.trackName, let artist_Name = item.artistName, let artwork_Url60 = item.artworkUrl60
{
let musics = Music(trackName: track_Name, artistName: artist_Name, artworkUrl60: artwork_Url60)
self.musicArray.append(musics)
print(musics.artistName!,"-", musics.trackName!)
}
}
DispatchQueue.main.async
{
self.tableView.reloadData()
}
}
catch let jsonError
{
print("Error:", jsonError)
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching
{
return filteredData.count
}
else
{
return mArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell", for: indexPath) as! ItunesDataTableViewCell
if isSearching
{
cell.lblDesc?.text = filteredData[indexPath.row].artistName
cell.lblSongDesc?.text = filteredData[indexPath.row].trackName
let imgString = filteredData[indexPath.row].artworkUrl60!
let imgUrl:URL = URL(string: imgString)!
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imgUrl)!
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
cell.imgArt?.image = image
}
}
}
else
{
cell.lblDesc?.text = mArray[indexPath.row].artistName
cell.lblSongDesc?.text = mArray[indexPath.row].trackName
let imgString = mArray[indexPath.row].artworkUrl60!
let imgUrl:URL = URL(string: imgString)!
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imgUrl)!
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
cell.imgArt?.image = image
}
}
}
return cell
}
}
Please clean up your code 😉: You have two different arrays mArray and musicArray.
You are populating musicArray in Search but mArray is used as data source.
Why do you create new Music items from Music items? You can reduce the code to
let apiressults = try JSONDecoder().decode(ApiResults.self, from: data)
self.mArray = apiressults.results
DispatchQueue.main.async {
self.tableView.reloadData()
}
Please change your code in the cellForRowAt delegate method to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell", for: indexPath) as! ItunesDataTableViewCell
let tempArray: [Music] = isSearching ? filteredData : musicArray
cell.lblDesc?.text = tempArray[indexPath.row].artistName
cell.lblSongDesc?.text = tempArray[indexPath.row].trackName
guard let imgString = tempArray[indexPath.row].artworkUrl60,
let imgUrl = URL(string: imgString) else {
// Handle properly the fact that there's no image to display
return cell
}
// Review this code as I'm not sure about this double dispatch
// However, please, no force unwrap optionals (!)
DispatchQueue.global(qos: .userInitiated).async {
do {
let imageData = try Data(contentsOf: imgUrl)
DispatchQueue.main.async {
let image = UIImage(data: imageData)
cell.imgArt?.image = image
}
} catch let error {
print("Error with the image URL: ", error)
}
}
return cell
}
See how you don't repeat your code that way?
Furthermore you were not using the right music array, or we don't have all the information to assess what is wrong with this mix of mArray and musicArray.

Index out of range during tableView.reloadData

I am having performance issues and index out of range errors during some refreshes. Not all, but I am struggling to troubleshoot it.
Basically, the app will collect data from an RSS feed and display it in a tableview. This is my first time using the libraries that I have, and my first time trying to properly make tableview app.
I believe the refresh error has to do with where I am refresh() as well as where I am calling projects.removeAll(). I have tried moving them to other places and I still get the refresh issue.
I am not sure why all of a sudden I have scrolling lag. If anyone has time to quickly review my code and give me suggestions, as well as try and help me fix the refresh section of the code (to fix the array out of bounds error) that would be amazing.
Thanks.
Here is my complete code.
//
// FirstViewController.swift
//
//
import UIKit
import SWXMLHash
import Alamofire
import SafariServices
class FirstViewController: UITableViewController, SFSafariViewControllerDelegate {
var projects = [[String]]()
var xmlToParse = String()
var postTitle = String()
var postLink = String()
var postAuthor = String()
var postThumbnail = String()
var postComments = String()
var postPublishDate = String()
var postVotes = String()
var postVotesNeg = String()
var dateFormatter = DateFormatter()
override func viewDidLoad() {
super.viewDidLoad()
httpRequest("https://www.test.com/rss") { response in
}
// set up the refresh control
self.refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
self.tableView?.addSubview(refreshControl!)
self.dateFormatter.dateStyle = DateFormatter.Style.short
self.dateFormatter.timeStyle = DateFormatter.Style.long
}
func setup() {
tableView.reloadData()
print("Refreshed")
}
#objc func refresh(sender:AnyObject) {
let now = NSDate()
let updateString = "Last Updated at " + self.dateFormatter.string(from: now as Date)
self.refreshControl?.attributedTitle = NSAttributedString(string: updateString)
if (self.refreshControl?.isRefreshing)!
{
self.refreshControl?.endRefreshing()
projects.removeAll()
}
httpRequest("https://www.test.com/rss") { response in
}
}
func enumerate(indexer: XMLIndexer, level: Int) {
for child in indexer.children {
let name = child.element!.name
print("\(level) \(name)")
enumerate(indexer: child, level: level + 1)
}
}
func httpRequest(_ section: String, completion: #escaping (String) -> Void) {
Alamofire.request(section, method: .get).responseString { response in
self.xmlToParse = response.result.value!
let xml = SWXMLHash.parse(self.xmlToParse)
for elem in xml["rss"]["channel"]["item"].all {
self.postTitle = elem["title"].element!.text
self.postLink = elem["link"].element!.text
self.postAuthor = elem["dc:creator"].element!.text
self.postThumbnail = (elem["media:thumbnail"].element?.attribute(by: "url")?.text)!
self.postComments = elem["comments"].element!.text
self.postPublishDate = elem["pubDate"].element!.text
self.postVotes = (elem["test:meta"].element?.attribute(by: "votes-pos")?.text)!
self.postVotesNeg = (elem["test:meta"].element?.attribute(by: "votes-neg")?.text)!
self.projects.append([self.postTitle, self.postLink, self.postAuthor, self.postThumbnail, self.postPublishDate, self.postComments, self.postVotes, self.postVotesNeg])
}
completion(response.result.value!)
self.setup()
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let url = URL(string: self.projects[indexPath.row][1]) {
let vc = SFSafariViewController(url: url)
vc.delegate = self
present(vc, animated: true)
}
}
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
dismiss(animated: true)
}
func makeAttributedString(title: String, subtitle: String) -> NSAttributedString {
let titleAttributes = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .headline), NSAttributedStringKey.foregroundColor: UIColor.black]
let subtitleAttributes = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: .subheadline), NSAttributedStringKey.foregroundColor: UIColor.gray]
let titleString = NSMutableAttributedString(string: "\(title)\n", attributes: titleAttributes)
let subtitleString = NSAttributedString(string: subtitle, attributes: subtitleAttributes)
titleString.append(subtitleString)
return titleString
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return projects.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let project = projects[indexPath.row]
cell.textLabel?.attributedText = makeAttributedString(title: project[0], subtitle: "Author: " + project[2] + "\nPost Date: " + project[4] + "\nUpvotes: " + project[6] + "\nDownvotes: " + project[7] )
let imageURL = URL(string: project[3])
let data = try? Data(contentsOf: imageURL!)
if data != nil {
let image = UIImage(data: data!)
cell.imageView?.image = image
}
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Wrong indexPathForSelectedRow when using prepareForSegue

I'm trying to fill a tableView with some products from my MySQL database (using a PHP POST file) and at the moment everything is fine, but when I select a "Cell", the prepareForSegue is triggered, but the indexPathForSelectedRow is wrong, so it's showing a different product.
Here is my full code, I hope you can tell me something because I don't know why is this happening, I have tried a lot of things and I'm out of options...!
TableViewController.swift
import UIKit
class ListadoBuscarResultadosTableViewController: UITableViewController {
var option: String = ""
var productos = [Producto]()
var refreshControl2:UIRefreshControl!
var imageCache = [String:UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
requestPost()
title = self.option
tableView.allowsMultipleSelection = true
tableView.scrollsToTop = true
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
func refresh(sender:AnyObject) {
requestPost()
self.refreshControl2.endRefreshing()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return productos.count
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// Define the initial state (Before the animation)
cell.alpha = 0.25
// Define the final state (After the animation)
UIView.animateWithDuration(1.0, animations: { cell.alpha = 1 })
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// try to reuse cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! BuscarCellTableViewCell
cell.selectionStyle = .None
//cell.nombre.text = productos[indexPath.row].nombre
//cell.marca.text = productos[indexPath.row].marca
//println(cell.nombre.text)
// get the deal image
let currentImage = productos[indexPath.row].imagen
let unwrappedImage = currentImage
var image = self.imageCache[unwrappedImage]
let imageUrl = NSURL(string: productos[indexPath.row].imagen)
// reset reused cell image to placeholder
cell.imagen.image = UIImage(named: "")
// async image
if image == nil {
let request: NSURLRequest = NSURLRequest(URL: imageUrl!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
if error == nil {
image = UIImage(data: data)
self.imageCache[unwrappedImage] = image
dispatch_async(dispatch_get_main_queue(), {
cell.imagen.image = image
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
})
}
else {
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
}
})
}
else {
cell.imagen.image = image
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
}
return cell
}
func requestPost () {
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.web.es/productos_by_category.php")!)
request.HTTPMethod = "POST"
let postString = "category="+option
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
//println("error=\(error)")
return
}
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)!
// JSON RESULTADO ENTERO
//println("responseString = \(responseString)")
self.productos = self.parseJsonData(data)
// Reload table view
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
task.resume()
}
func parseJsonData(data: NSData) -> [Producto] {
var productos = [Producto]()
var error:NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
// Return nil if there is any error
if error != nil {
println(error?.localizedDescription)
}
// Parse JSON data
let jsonProductos = jsonResult?["lista_productos"] as! [AnyObject]
for jsonProducto in jsonProductos {
let producto = Producto()
producto.nombre = jsonProducto["nombre"] as! String
producto.imagen = jsonProducto["imagen"] as! String
producto.marca = jsonProducto["marca"] as! String
producto.distribuidor = jsonProducto["distribuidor"] as! String
producto.linea = jsonProducto["linea"] as! String
producto.precio = jsonProducto["precio"] as! String
productos.append(producto)
}
return productos
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "verProducto" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let destinationController = segue.destinationViewController as! MarcaProductoViewController
//println(productos[indexPath.row].nombre)
println(indexPath)
destinationController.nombre = productos[indexPath.row].nombre
}
}
}
Thanks in advance,
Regards.
try this way...
if segue.identifier == "verProducto"{
if let indexPath = tableView.indexPathForCell(sender as! BuscarCellTableViewCell){
var detailsVC = segue.destinationViewController as! MarcaProductoViewController
}
}