Manipulate YouTube Playlist API in tableview using Alamofire - ios

I have searched around to find an answer for my issue, but I had no luck. I'm new in coding, especially with Swift 3.0.
I'm trying to parse a YouTube playlist dynamically in a tableview using Alamofire cocoa pod in my project. My project contains: a viewcontroller called "videosViewController" which holds the tableview, a class called "Video", which holds the items I'm parsing from youtube API, and another class called "VideoModel" holds the method to pare those items. When I run my project the console parse the items successfully, but then the project crashes at the line of code:
for video in (data["items"] as? NSDictionary)!
with "Could not cast value of type '__NSArrayI' (0x10d2ebd88) to 'NSDictionary' (0x10d2ec288)." error as shown below
Project crash
Console details
And here the snippet of code I used:
videosViewController:
import UIKit
class videosViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var videos:[Video] = [Video]()
var selectedVideo: Video?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let model = VideoModel()
model.fetchVideos()
self.tableView.dataSource = self
self.tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return (self.view.frame.size.width / 320) * 180
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BasicCell")!
let videoTitle = videos[indexPath.row].videoTitle
let label = cell.viewWithTag(2) as! UILabel
label.text = videoTitle
let videoThumbnailUrlString = "https://i1.ytimg.com/vi/" + videos[indexPath.row].videoId + "/maxresdefault.jpg"
let videoThumbnailUrl = NSURL(string: videoThumbnailUrlString)
if videoThumbnailUrl != nil {
let request = URLRequest(url: videoThumbnailUrl! as URL)
let session = URLSession.shared
let task = session.dataTask(with: request,
completionHandler: { (data:Data?,
response:URLResponse?,
error:Error?) -> Void in
DispatchQueue.main.async {
let imageView = cell.viewWithTag(1) as! UIImageView
imageView.image = UIImage(data: data!)
}
})
task.resume()
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedVideo = self.videos[indexPath.row]
self.performSegue(withIdentifier: "goToDetail", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let detailView = segue.destination as! videoDetailViewController
detailView.selectedVideo = self.selectedVideo
}
}
The Video class:
import UIKit
class Video: NSObject {
var videoId:String = ""
var videoTitle:String = ""
var videoDescription:String = ""
var videoThumbnailURL = ""
}
And the VideoModel class:
import UIKit
import Alamofire
class VideoModel: NSObject {
let parameters: Parameters = ["part":"snippet","playlistId":"PLMRqhzcHGw1ZRUB86rmNqG15Sr5jV-2NU","key":"AIzaSyDdNXhz3H7ifXB-qfOVakz0Xps2Y-kP0R0"]
var videoArray = [Video]()
func fetchVideos() {
Alamofire.request("https://www.googleapis.com/youtube/v3/playlistItems", method: .get, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(let JSON):
print("Success with JSON: \(JSON)")
if let data = response.result.value as? [String: AnyObject] {
// print(response.result.value)
var arrayOfVideos = [Video]()
for video in (data["items"] as? NSDictionary)! {
let videoObj = Video()
videoObj.videoId = (video.value as? NSDictionary)?["snippet.resourceId.videoId"] as? String ?? ""
videoObj.videoTitle = (video.value as? NSDictionary)?["snippet.title"] as? String ?? ""
videoObj.videoDescription = (video.value as? NSDictionary)?["snippet.description"] as? String ?? ""
videoObj.videoThumbnailURL = (video.value as? NSDictionary)?["snippet.thumbnails.maxres.url"] as? String ?? ""
print(video)
// You need to parse the items into the video data
arrayOfVideos.append(videoObj)
}
self.videoArray = arrayOfVideos
// }
}
case .failure(let error):
print("Request failed with error: \(error)")
}
}
}

Replace
as? NSDictionary
with
as? [String:Any]
in
for video in (data["items"] as? NSDictionary)!
Bcs: You have to cast type Any to Swift dictionary type [String:Any].
if let JSON = response.result.value as? [String : Any] {
if let items = JSON["items"] as? [[String : Any]] {
for video in items {
//Other code
}
}
}

Related

Value of type 'UITableViewCell' has no member 'name' in multiple TableView

I tried to make ViewController with two TableView but meet the problem.
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableTN: UITableView!
#IBOutlet weak var tableMainNews: UITableView!
var topnews: [TopNews]? = []
var mainnews: [Mainnewsfeed]? = []
override func viewDidLoad() {
super.viewDidLoad()
TopNewsJSON()
MainNewsJSON()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell?
if tableView == self.tableTN {
cell = tableView.dequeueReusableCell(withIdentifier: "topnewsCell", for:indexPath) as! TopNewsCell
cell!.imgTN!.downloadImage(from: (self.topnews?[indexPath.item].image!)!)
cell!.titleTN!.text = self.topnews?[indexPath.item].headline
}
if tableView == self.tableMainNews {
cell = tableView.dequeueReusableCell(withIdentifier: "mainnewsCell", for:indexPath) as! MainNewsCell
cell!.mainnews_title!.text = self.mainnews?[indexPath.item].headline
}
return cell!
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count:Int?
if tableView == self.tableTN {
count = self.topnews!.count
}
if tableView == self.tableMainNews {
count = self.mainnews!.count
}
return count!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(indexPath)
}
func TopNewsJSON () {
let urlRequest = URLRequest(url: URL(string: "https://sportarena.com/wp-api/topnews2018/top/")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error as Any)
return
}
self.topnews = [TopNews]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
//print(json)
let TN = TopNews()
let jarray = json["top-news"] as! NSArray
let jarray1 = jarray[0] as? [String: AnyObject]
if let ID = jarray1!["ID"] as? String,
let title = jarray1!["title"] as? String,
let img = jarray1!["img"] as? String {
TN.headline = title
TN.image = img
TN.id = ID
}
self.topnews?.append(TN)
DispatchQueue.main.async {
self.tableTN.reloadData()
}
} catch let error {
print(error)
}
}
task.resume()
}
func MainNewsJSON () {
let urlRequest = URLRequest(url: URL(string: "anyurl")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error as Any)
return
}
//self.mainnews = [MainNews]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
let jarray = json["general-news"] as! NSArray
let jarray1 = jarray[0]
for jarray1 in jarray1 as! [[String: Any]] {
let MNF = Mainnewsfeed()
if let ID = jarray1["id"],
let title = jarray1["title"],
let time = jarray1["datetime"] {
MNF.headline = title as? String
MNF.id = ID as? String
MNF.time = time as? String
}
self.mainnews?.append(MNF)
DispatchQueue.main.async {
self.tableMainNews.reloadData()
}
}
} catch let error {
print(error)
}
}
task.resume()
}
}
}
After three lines as cell!.titleTN!.text = self.topnews?[indexPath.item].headline and others display error: "Value of type 'UITableViewCell' has no member 'titleTN'" (or also 'imgTN' and 'mainnews_title')
Where the error? What I need to change in my code?
Please help me.
You can try
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableTN {
let cell = tableView.dequeueReusableCell(withIdentifier: "topnewsCell", for:indexPath) as! TopNewsCell
cell.imgTN!.downloadImage(from: (self.topnews?[indexPath.item].image!)!)
cell.titleTN!.text = self.topnews?[indexPath.item].headline
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "mainnewsCell", for:indexPath) as! MainNewsCell
cell.mainnews_title!.text = self.mainnews?[indexPath.item].headline
return cell
}
}

pass json id from one table view controller into another table view controller in swift 3

I already new in swift 3 and objetive c, right now I am stuck into how can I pass the id of each row to another table view controller when the user tap in the row the user want to go.
Here is the json data firstFile:
[
{"id_categoria":"1","totalRows":"323","nombre_categoria":"Cirug\u00eda"},
{"id_categoria":"2","totalRows":"312","nombre_categoria":"Med Interna"},
{"id_categoria":"3","totalRows":"6","nombre_categoria":"Anatomia"},
{"id_categoria":"4","totalRows":"24","nombre_categoria":"Anestesiologia"},
...]
Here is my first table view controller:
import UIKit
class CatMedVC: UIViewController, UITableViewDataSource {
#IBAction func volver(_ sender: Any) { }
#IBOutlet weak var listaCategoria: UITableView!
var fetchedCategoria = [Categoria]()
override func viewDidLoad() {
super.viewDidLoad()
listaCategoria.dataSource = self
parseData()
}
override var prefersStatusBarHidden: Bool{
return true
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedCategoria.count
}
public func tableView(_ tableView: UITableView, cellForRowAt IndexPath: IndexPath) ->
UITableViewCell {
let cell = listaCategoria.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = fetchedCategoria[IndexPath.row].esp
cell?.detailTextLabel?.text = fetchedCategoria [IndexPath.row].totalRows
return cell!
}
func parseData() {
let url = "http://www.url.com/firstFile.php" //in json format
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
//print(fetchedData)
for eachFetchedCategoria in fetchedData {
let eachCategoria = eachFetchedCategoria as! [String : Any]
let nombre_categoria = eachCategoria["nombre_categoria"] as! String
let totalRows = eachCategoria["totalRows"] as! String
let id_categoria = eachCategoria["id_categoria"] as! String
self.fetchedCategoria.append(Categoria(nombre_categoria: nombre_categoria, totalRows: totalRows, id_categoria: id_categoria))
}
//print(self.fetchedCategoria)
self.listaCategoria.reloadData()
}
catch {
print("Error 2")
}
}
}
task.resume()
}
}
class Categoria {
var nombre_categoria : String
var totalRows : String
var id_categoria : String
init(nombre_categoria : String, totalRows : String, id_categoria : String) {
self.nombre_categoria = nombre_categoria
self.totalRows = totalRows
self.id_categoria = id_categoria
}
}
So I need pass the id_categoria String into the another table view to show the data for the id selected previously...here I don't know how to do it...I have the json file waiting for the id selected previously..but I don't know how to catch it into the url
Here the second table view:
import UIKit
class EspMedVC: UITableViewController {
var TableData:Array< String > = Array < String >()
var EspecialidadArray = [String]()
#IBAction func volver(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.url.com/secondFile.php?id=") // Here I need to put the id_categoria String in json format
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", 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 nombre_especialidad = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let nombre_esp_obj = nombre_especialidad[i] as? NSDictionary
{
if let nombre_especialidad = nombre_esp_obj["subesp"] as? String
{
if let totalRows = nombre_esp_obj["totalRows"] as? String
{
TableData.append(nombre_especialidad + " [" + totalRows + "]")
}
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
func do_table_refresh()
{
self.tableView.reloadData()
}
}
This is a rough guide, please search for the methods in the documentation or here at other questions inside stackoverflow.
1) Add a variable inside your EspMedVC that will hold the "id_categoria String" that should be displayed.
2) Add a variable inside your CatMedVC that will hold the "id_categoria String" that the user selected.
3) Implement the "didSelectRow" delegate method from your tableview inside the "CatMedVC", inside this method you should set the variable set on step 2.
4) Implement the "prepareForSegue" method inside your CatMedVC, inside the the implementation you should retrieve the destination VC, cast it to "EspMedVC" and set the variable from step 1.
5) On the "viewDidLoad" from EspMedVC you can now use the variable set on step 2 to query your JSON and update the table view accordingly.

Swift 3 / Alamofire : I am not able to fetch the data on UITableView

I am using Alamofire and trying to fetch data on my tableview, however I am not able to get the data. When I use the cmd Print, its showing me the data but not able to fetch the data. How can I fetch the data on my tableview?
Please find the code below:-
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSURLConnectionDelegate {
//let myarray = ["item1", "item2", "item3"]
var tableData = Array<Group>()
var arrRes = [[String:AnyObject]]() //Array of dictionary
var group = [Group]()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
loadGroups()
}
func loadGroups(){
let testhappyhour:Group = Group(tempName: "TEST", tempID: "TESST", icons: "TEST", tempgbcount: "TEST")
self.group.append(testhappyhour)
//let groupQuery:String = "http://jsonplaceholder.typicode.com/users"
Alamofire.request("http://jsonplaceholder.typicode.com/users").responseJSON
{ response in switch response.result {
case .success(let JSON):
let response = JSON as! NSArray
for item in response { // loop through data items
let obj = item as! NSDictionary
let happyhour = Group(tempName:obj["NAME"] as! String, tempID:obj["id"] as! String, icons:obj["icon"] as! String, tempgbcount:obj["TOTAL"] as! String)
self.group.append(happyhour)
}
self.tableview.reloadData()
case .failure(let error):
print("Request failed with error: \(error)")
}
}
}
func convertToArray(text: String) -> [Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableview.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//return myarray.count
return arrRes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "groupCell", for: indexPath) as! UITableViewCell
// cell.textLabel?.text = myarray[indexPath.item]
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "groupCell")!
var dict = arrRes[indexPath.row]
cell.textLabel?.text = dict["NAME"] as? String
cell.detailTextLabel?.text = dict["TOTAL"] as? String
return cell
}
}
Thank you!!
Need to change loadGroups function like this
func loadGroups(){
let testhappyhour:Group = Group(tempName: "TEST", tempID: "TESST", icons: "TEST", tempgbcount: "TEST")
self.group.append(testhappyhour)
Alamofire.request("http://jsonplaceholder.typicode.com/users").responseJSON
{ response in switch response.result {
case .success(let JSON):
let response = JSON as! NSArray
for item in response { // loop through data items
let obj = item as! NSDictionary
let happyhour = Group(tempName:obj["NAME"] as! String, tempID:obj["id"] as! String, icons:obj["icon"] as! String, tempgbcount:obj["TOTAL"] as! String)
self.group.append(happyhour)
self.arrRes.append(obj) // ADD THIS LINE
}
self.tableview.reloadData()
case .failure(let error):
print("Request failed with error: \(error)")
}
}
}
Array 'group' is appended with the Alamofire responses, but Array 'arrRes' is used as the table view data source. If you use self.group instead of arrRes in the data source methods, the table should update with the new groups received in the Alamofire response.

How do I fix laggy UITableView scrolling performance when downloading JSON?

In my application, I download a JSON file off of the internet and fill up a UITableView with items from the file. It does work well, and there are no problems or errors, but the scrolling performance is very laggy, and the UI glitches out a tiny bit.
I assume this is because of the images that I'm downloading from the JSON file, so I've looked into multi-threading, but I don't think I am doing it right because it does load much faster, but scrolling performance is still the same as before.
Can somebody please tell me how to fix this? This UITableView is the most important thing in the app, and I have been spending much time on trying to fix it. Thank you!
Here is my code-
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var nameArray = [String]()
var idArray = [String]()
var ageArray = [String]()
var genderArray = [String]()
var descriptionArray = [String]()
var imgURLArray = [String]()
let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
final let urlString = "https://pbsocfilestorage.000webhostapp.com/jsonDogs.json"
override func viewDidLoad() {
super.viewDidLoad()
self.downloadJsonWithURL()
// Activity Indicator
myActivityIndicator.center = view.center
myActivityIndicator.hidesWhenStopped = true
myActivityIndicator.startAnimating()
view.addSubview(myActivityIndicator)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func downloadJsonWithURL() {
let url = NSURL(string:urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: {(data, response, error) ->
Void in
print("Good so far...")
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!.value(forKey: "dogs"))
if let dogArray = jsonObj!.value(forKey: "dogs") as? NSArray {
print("Why u no work!")
for dog in dogArray {
if let dogDict = dog as? NSDictionary {
if let name = dogDict.value(forKey: "name") {
self.nameArray.append(name as! String)
}
if let name = dogDict.value(forKey: "id") {
self.idArray.append(name as! String)
}
if let name = dogDict.value(forKey: "age") {
self.ageArray.append(name as! String)
}
if let name = dogDict.value(forKey: "gender") {
self.genderArray.append(name as! String)
}
if let name = dogDict.value(forKey: "image") {
self.imgURLArray.append(name as! String)
}
if let name = dogDict.value(forKey: "description") {
self.descriptionArray.append(name as! String)
}
OperationQueue.main.addOperation ({
self.myActivityIndicator.stopAnimating()
self.tableView.reloadData()
})
}
}
}
}
}).resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imgURL = NSURL(string: imgURLArray[indexPath.row])
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell") as! TableViewCell
URLSession.shared.dataTask(with: (imgURL as! URL), completionHandler: {(data, resp, error) -> Void in
if (error == nil && data != nil) {
OperationQueue.main.addOperation({
cell.dogNameLabel.text = self.nameArray[indexPath.row]
cell.idLabel.text = self.idArray[indexPath.row]
cell.ageLabel.text = self.ageArray[indexPath.row]
cell.genderLabel.text = self.genderArray[indexPath.row]
print("Cell info was filled in!")
if imgURL != nil {
let data = NSData(contentsOf: (imgURL as? URL)!)
cell.dogImage.image = UIImage(data: data as! Data)
}
})
}
}).resume()
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDog" {
if let indexPath = self.tableView.indexPathForSelectedRow{
let detailViewController = segue.destination as! DetailViewController
detailViewController.imageString = imgURLArray[indexPath.row]
detailViewController.nameString = nameArray[indexPath.row]
detailViewController.idString = idArray[indexPath.row]
detailViewController.ageString = ageArray[indexPath.row]
detailViewController.descriptionString = descriptionArray[indexPath.row]
detailViewController.genderString = genderArray[indexPath.row]
}
}
}
}
There is a big mistake. You are loading data with dataTask but you aren't using that returned data at all. Rather than you are loading the data a second time with synchronous contentsOf. Don't do that.
And don't update the labels in the asynchronous completion block. The strings are not related to the image data.
This is more efficient:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let imgURL = URL(string: imgURLArray[indexPath.row])
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! TableViewCell
cell.dogNameLabel.text = self.nameArray[indexPath.row]
cell.idLabel.text = self.idArray[indexPath.row]
cell.ageLabel.text = self.ageArray[indexPath.row]
cell.genderLabel.text = self.genderArray[indexPath.row]
print("Cell info was filled in!")
URLSession.shared.dataTask(with: imgURL!) { (data, resp, error) in
if let data = data {
OperationQueue.main.addOperation({
cell.dogImage.image = UIImage(data: data)
})
}
}.resume()
return cell
}
Note: You are strongly discouraged from using multiple arrays as data source. It's very error-prone. Use a custom struct or class. And create imgURLArray with URL instances rather than strings. This is also much more efficient.
Nevertheless, you should use a download manager which caches the images and cancels downloads if a cell goes off-screen. At the moment each image is downloaded again when the user scrolls and cellForRow is called again for this particular cell.

error with maindata in swift 3

I have this problem (Type any has no subscript members) in this line `
import Foundation
import UIKit
import WebKit
import GoogleMobileAds
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,GADBannerViewDelegate {
#IBOutlet weak var BannerView: GADBannerView!
#IBOutlet var tableView: UITableView!
#IBAction func refresh(_ sender: AnyObject) {
get()
}
var values:NSArray = []
override func viewDidLoad() {
super.viewDidLoad();
let request = GADRequest()
request.testDevices = [kGADSimulatorID]
BannerView.delegate = self
BannerView.adUnitID = ""
BannerView.rootViewController = self
BannerView.load(request)
get();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func get(){
let url = URL(string: "http://www.X.php")
let data = try? Data(contentsOf: url!)
values = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SpecialCell
let maindata = values[(indexPath as NSIndexPath).row]
cell.info!.text = maindata ["Info"] as String?
return cell;
}
}
image
thank you all..
First of all declare the data source array as Swift Array. Foundation NSArray has no type information and doesn't help Swift's strong type system at all.
var values = [[String:Any]]()
Then load the data asynchronously(!) and reload the table view on the main thread
func get() {
let url = URL(string: "http://www.X.php")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
} else {
do {
self.values = try JSONSerialization.jsonObject(with: data!, options: []) as! [[String:Any]]
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
task.resume()
}
Then in cellForRow assign the value
let maindata = values[indexPath.row]
cell.info!.text = maindata["Info"] as? String
Now the compiler knows all subscripted types and the error goes away.
I believe you have to give mainData a type like this:
let maindata = values[(indexPath as NSIndexPath).row] as? [String:Any]
and then make sure mainData actually contains a value
if let info = mainData?["Info"] as? String {
cell.info!.text = info
}

Resources