How to get json data for passing it by segue - ios

I try to get json data while URLSession.dataTask is running to set segue .(each json data as sender)
So First, I made my own Class Array productList = [Product]().
Next, I call getJsonData() and inside of that, I set URLSession.dataTask method. So I got Parsed json data. However, When I try to save that json data(append each data to productList) from dataTask completionHandler, it cannot save correctly.(result productList is [])
I want to pass parsed json data by segue. How can I do this?
edited --
class MainVC: UITableViewController {
var productList = [Product]()
override func viewDidLoad() {
super.viewDidLoad()
getJsonData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell {
let product = productList[indexPath.row]
cell.configureCell(product)
return cell
} else {
return UITableViewCell()
}
}
func getJsonData() {
let url = URL(string: "http://demo7367352.mockable.io")
let request = URLRequest(url: url!)
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) in
do {
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
guard let rawItem = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
if let fineItem = rawItem["goods"] as? [[String:Any]] {
for item in fineItem {
let eachProduct = Product(title: "", price: 0)
let title = item["TITLE"]
let price = item["PRICE"]
let regDate = item["REGDATE"]
let description = item["DESCRIPTION"]
let iconURL = item["ICON_URL"]
let images = item["IMAGES"]
if let title = title as? String {
eachProduct.title = title
}
if let price = price as? String {
eachProduct.price = Int(price)!
}
DispatchQueue.main.async(execute: {
self.productList.append(eachProduct)
self.tableView.reloadData()
})
}
}
} catch {
print("error trying to convert data to JSON")
return
}
})
task.resume()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProductDetail" {
if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
}
}
}
}
Right now, I can parse datas from URLSession DataTask. I want to implement segue of tableView to show detail. But productList is empty. So I cannot use prepareForSegue with productList[indexPath.row].
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProductDetail" {
if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
controller.product = productList[indexPath.row] // productList is nil.
}
}
}

You need to implement prepare(for:sender:) and pass the data there:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? MySecondViewController, indexPath = tableView.indexPathForSelectedRow {
controller.product = productList[indexPath.row]
}
}
The exact syntax will vary (what is the class name of your destination view controller) and you'd have to declare that product property in the destination, and the destination's viewDidLoad would need to use that property, but hopefully it illustrates the basic idea.
Some additional observations:
I'd suggest you inspect rawItem and make sure it's a dictionary that has a key called goods and that the value associated with that key is really an array of dictionaries. Without seeing your JSON, it's impossible to say what exactly is going wrong.
Also, consider:
if let fineItem = rawItem["goods"] as? [[String:Any]] {
...
}
If that fails, you'll never know. I might instead suggest:
guard let fineItem = rawItem["goods"] as? [[String:Any]] else {
print("goods not found or wrong type")
return
}
...
BTW, and unrelated to your problem at hand, it's a little dangerous to be mutating productList directly in the completion handler of your data task. Don't asynchronously mutate arrays from one thread that are read from another thread. Arrays are not thread-safe. The data task completion handler should build a local array and only when it's done, inside where you're dispatching the reload to the main queue, you should insert code to replace the productList with your local array before the table is reloaded.
Also, you're currently calling reloadData inside the parsing loop. You'd generally call it at the end of the parsing loop. Right now, if your data set had 100 rows, you'd be reloading the table 100 times.
The reference to data! is a bit dangerous. If you have no internet connection, data will be nil and your code will crash. I'd suggest:
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
And then you can replace the data! reference with data.

You aren't posting all of your code, but I believe that your mistake is that you are performing an asynchronous task and then immediately calling print on the array being modified. I wouldn't expect the array to be populated until the task is complete.
Does your tableView actually populate with results? Are you printing out the JSON to ensure that your data is matching properly? Are the errors being printed?
Edit:
To pass your data along the segue you need to retrieve your destinationViewController as a variable and pass the information to it. There is a method called prepareForSegue that allows you to handle the preliminary state before your action happens.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ExampleVC
vc.setProducts(productList)
}
Something like that. Obviously changing your class and variable names

I solved my Problem and here is my final code.
class MainVC: UITableViewController {
var productList = [Product]()
override func viewDidLoad() {
super.viewDidLoad()
getJsonData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as? ItemCell {
let product = productList[indexPath.row]
cell.configureCell(product)
return cell
} else {
return UITableViewCell()
}
}
// parsing
func getJsonData() {
let url = URL(string: "http://demo7367352.mockable.io")
let request = URLRequest(url: url!)
let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) in
do {
guard let data = data, error == nil else {
print("network request failed: error = \(error)")
return
}
guard let rawItem = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
guard let fineItem = rawItem["goods"] as? [[String:Any]] else {
print("goods not found or wrong type")
return
}
for item in fineItem {
let eachProduct = Product(title: "", price: 0)
let title = item["TITLE"]
let price = item["PRICE"]
if let title = title as? String {
eachProduct.title = title
}
if let price = price as? String {
eachProduct.price = Int(price)!
}
self.productList.append(eachProduct)
}
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
} catch {
print("error trying to convert data to JSON")
return
}
})
task.resume()
}
// segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToProductDetail" {
if let controller = segue.destination as? DetailVC, let indexPath = tableView.indexPathForSelectedRow {
controller.product = productList[indexPath.row]
}
}
}
}

Related

tableview is not loading sometimes , is there anyway to refresh it?

I am new in swift and have implemented a tableview with url session data and it works fine except the images are not loading and sometimes the data comes late and the tableview is empty.
I put http://localhost:3000/nameofimage.png , I get my image but when in tableview it did not work
import UIKit
import Kingfisher
class BikelistViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var bikes = [Bike]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bikes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "mCellBike")
let contentView = cell?.contentView
let imageView = contentView?.viewWithTag(1) as! UIImageView
let label = contentView?.viewWithTag(2) as! UILabel
DispatchQueue.main.async {
label.text = self.bikes[indexPath.row].model
let url = URL(string: "http://localhost:3000/"+self.bikes[indexPath.row].image)
imageView.kf.setImage(with: url)
}
return cell!
}
//passage de parametres entre les controleurs
//cell OnclickListener
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let bike = bikes[indexPath.row]
performSegue(withIdentifier: "mBikeDetails" , sender: bike) //passage de variable locale)
}
/* prepare est pour passer les parametres */
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "mBikeDetails" {
let bike = sender as! Bike
let destination = segue.destination as! BikeDetailsViewController
destination.id = bike.bike_id
destination.model = bike.model
destination.type = bike.type
destination.mprice = bike.price
destination.image = bike.image
}}
override func viewDidLoad() {
super.viewDidLoad()
//get
guard let url = URL(string: "http://localhost:3000/bikes") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [[String:Any]]
self.bikes.removeAll()
for item in json {
let id = item["bike_id"] as! Int
let model = item["model"] as! String
let type = item["type"] as! String
let price = item["price"] as! String
let image = item["image"] as! String
self.bikes.append(Bike(id: id,model: model,type: type,price: price,image: image))
}
for item in self.bikes {
print(item.image)
print("http://localhost:3000/"+item.image)
}
print(self.bikes)
}catch{
print(error)
}
}
}.resume()
// Do any additional setup after loading the view.
}
}
I am trying to make my images load in my tableview , my data is displaying except my images. and sometimes the data comes late and the tableview is empty
Is there anything I am missing here?
You are trying to fetch the data in main thread that is why it is lagging. Just let the main thread create cell objects, dont implement any network action here so this is wrong in your code :
imageView.image = UIImage(named: "http://localhost:3000/"+bikes[indexPath.row].image)
You should not do network actions in your main thread. If you wish either you can use use third party library such as KingFisher or using your assets folder directly. Your tableview will be relax if you do it like this:
imageView.image = UIImage(named: "happy_bikeimage_coming_from_assets_folder")
For example fetching data background :
DispatchQueue.global(qos: .userInitiated).async {
if let url = URL(string: urlString) {
if let data = try? Data(contentsOf: url) {
self.parse(json: data)
return
}
}
}
You can use kingFisher if you are loading the image online check this:
https://stackoverflow.com/a/65114085/14437411
or you can use it in your assets folder and call the image by its name normally :
DispatchQueue.main.async {
UIImage(named: "imageName.extension")
}

TableViewController populates on search, but won't load initial view

Swift 5, iOS 12, Xcode 10
I finally implemented a search bar on my ListingsViewController and it's working really well -- but the initial data isn't populating into the table.
I realize that this is a clumsy implementation, but I'm learning as I go and I'm using only code that I can understand. I've been at this for two days -- I've tried creating a Struct and bringing in the data that way, but I can't even get an Array. I've tried bringing it in as an NSArray and an Array and an Object, but either I can't get the initial table to load, or I can't get data to parse out at all, or I can't get the search to work.
I suspect it has something to do with how, when or where I'm calling the loadData() function but I just can't figure it out.
class ListingsViewController: UITableViewController, UISearchResultsUpdating {
var tableData = [String]()
var filteredTableData = [String]()
var resultSearchController = UISearchController()
func updateSearchResults(for searchController: UISearchController) {
filteredTableData.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (tableData as NSArray).filtered(using: searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
tableData = [String]()
loadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
tableView.tableHeaderView = controller.searchBar
return controller
})()
tableView.reloadData()
}
func loadData() {
guard let url = URL(string: "--------------") else {return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
guard let jsonArray = jsonResponse as? [[String:Any]] else { return }
for dic in jsonArray {
self.tableData.append(dic["name"] as! String)
// self.feedItems.append(Listing(id: (dic["id"] as! String), city_id: (dic["city_id"] as! String), category: (dic["category"] as! String), sub_category: (dic["sub_category"] as! String), name: (dic["name"] as! String), phone: (dic["phone"] as! String), email: (dic["email"] as! String), website: (dic["website"] as! String), address: (dic["address"] as! String), comment: (dic["comment"] as! String), recommendedby: (dic["recommendedby"] as! String)))
}
} catch let parsingError {
print("Error", parsingError)
}
}
self.tableView.reloadData()
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (resultSearchController.isActive) {
return filteredTableData.count
} else {
return tableData.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.textColor = .white
cell.backgroundColor = .black
cell.tintColor = .lightText
if (resultSearchController.isActive) {
cell.textLabel?.text = filteredTableData[indexPath.row]
return cell
} else {
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
}
}
I'm expecting the entire list of Listings to appear when I switch to the ListingsViewController from the tab bar. Instead, I get a blank table.
HOWEVER, If I tap into the search bar and start typing, though, I get matching results -- and when I cancel the search, I can see all of the results in the table.
(Also, when I tap into the search bar, my navigation bar goes away and doesn't come back, even when I cancel the search. Even if I switch to a different tab and come back. Haven't been able to figure that one out.)
You are missing the delegate and data source for the table view
add this:
class ListingsViewController: UITableViewController, UISearchResultsUpdating,UITableViewDelegate,UITableViewDataSource {
//your class code here
}
and on your viewDidLoad add this:
self.tableView.delegate = self
self.tableView.dataSource = self
that should work, Please notice that after you called the delegate and data source your cell for row and numbers of rows functions will be called so make sure the array you are using is not nil by then
I saw you haven't been missing delegate and data source for the table view.
You must register delegate and data source for viewcontroler. where you will display data.
Please insert this code on your viewDidLoad
self.tableView.delegate = self
self.tableView.dataSource = self
Change your load day to this
func loadData() {
guard let url = URL(string: "--------------") else’s {return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
guard let jsonArray = jsonResponse as? [[String:Any]] else { return }
for dic in jsonArray {
self.tableData.append(dic[“name”] as! String
}
DispatchQueue.main.async {
print("call to reload data")
self.tableView.reloadData()
}
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
your are callig to reload data before the completion block, you can check it with the following print("calling to reloadData") then test my solution and see that works, make a print statamente for JsonArray to check where is called, you must reload the tableViewData after the async func is terminated.

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.

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.

IOS swift how can I populate my TableView with Json data being returned

I have just started working with Swift and am able to do some basic things. Right now I am trying to populate my UITableView with Json Data that I am successfully retrieving. Right now I have this simple Table that looks like this
That is a basic TableView that I was able to create with this code
#IBOutlet var StreamsTableView: UITableView!
let groceries = ["Fish","lobster","Rice","Beans"]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let mycell:UITableViewCell = StreamsTableView.dequeueReusableCell(withIdentifier: "prototype1", for: indexPath)
mycell.textLabel?.text = groceries[indexPath.row]
return mycell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return groceries.count
}
override func viewDidLoad() {
super.viewDidLoad()
StreamsTableView.dataSource = self
}
I now have a JsonRequest that I am completing successfully using this code below
override func viewDidLoad() {
super.viewDidLoad()
StreamsTableView.dataSource = self
// Do any additional setup after loading the view.
var names = [String]()
let urlString = "http://localhost:8000/streams"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Streams = parsedData["Streams"] as! [AnyObject]? {
for Stream in Streams {
if let post = Stream["post"] as? String {
names.append(post)
}
}
}
} catch let error as NSError {
print(error)
}
print(names)
}
}).resume()
}
What I essentially like to do is put the value of
let post = Stream["post"] as? String
inside the TableView instead of the Groceries array . As I stated before the value is coming back from the Json, I just have not found any way that I could put that value inside the TableView any help would be great . I am using swift 3.0 .
Add reloading data code
DispatchQueue.main.async {
StreamsTableView.reloadData()
}
just after your for loop
for Stream in Streams { ...
if let Streams = parsedData["Streams"] as! [AnyObject]? {
for Stream in Streams {
if let post = Stream["post"] as? String {
names.append(post)
}
}
}
StreamsTableView.reloadData()
After loop done
StreamsTableView.reloadData()
update:
mycell.textLabel?.text = groceries[indexPath.row]
to
mycell.textLabel?.text = names[indexPath.row]

Resources