I have this here structured:
struct Excercise: Codable {
let excerciseID: String
let excerciseName: String
let description: String
let intensity: Int
}
Then, that's the var:
var excercistList = [Excercise]()
And the cellforrowat:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExcerciseCell", for: indexPath)
cell.textLabel?.text = self.excercistList[indexPath.row].excerciseName
return cell
}
That's the URLSession:
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else{return}
do {
let excerciseList = try JSONDecoder().decode(Excercise.self, from: data)
print(excerciseList.excerciseName)
} catch let jsonErr {
print("Error", jsonErr)
}
}.resume()
I get the right results on the print, but nothing on the table.
What am I doing wrong?
first of all you have to populate your dataSource which is var excercistList = [Excercise](). After that you have to reload tableView.
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else{return}
do {
let excerciseListFromDecoder = try JSONDecoder().decode(Excercise.self, from: data)
self.excercistList = excerciseListFromDecoder
self.tableView.reloadData()
} catch let jsonErr {
print("Error", jsonErr)
}
}.resume()
You have to be sure that you have set correctly
tableView.delegate = self
tableView.dataSource = self
and the numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.excercistList.count
}
Related
I am using an endpoint that returns a JSON as response. The problem is response json is a huge data to process for me. From that I want to show all the Surah(englishName) name to display in the tableview. I tried my best as a new bee to iOS development. Please take a look at my snippet and let me know where i am doing wrong.
my Json data here:
ViewController code:
var surahName = [Surah]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: JSON parse
func parseJSON() {
let url = URL(string: "https://api.alquran.cloud/v1/quran/ar.alafasy")
guard url != nil else{
print("URL Founr Nill")
return
}
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil && data != nil{
do{
self.surahName = try JSONDecoder().decode([Surah].self, from: data!)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch{
print(error)
}
}
}.resume()
}
//MARK: Tableview delegate
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return surahName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:QuranAudioCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! QuranAudioCell
let arrdata = surahName[indexPath.section].data.surahs
cell.nameLbl.text = arrdata[indexPath.row].englishName
return cell
}
Problem is its not printing anything in the tablview.
Change first line as
var surahNames = [EnglishName]()
Inside do-catch block change
self.surahName = try JSONDecoder().decode([Surah].self, from: data!)
into
let response = try JSONDecoder().decode(Surah.self, from: data!)
self.surahName = response.data.surahs
Now inside cellForRowAtIndexPath do this
let surah = surahName[indexPath.row]
cell.nameLbl.text = surah.englishName
I can't figure out why the cells don't return with data.
I can parse normally using the Decodable, which means that is working.
I've been trying all the methods I find without success.
struct MuscleGroup: Decodable {
let ExcerciseID: String
let description: String
let excerciseName: String
let muscleGroup: String
}
class ExerciseListViewController: UITableViewController {
var muscleGroup = [MuscleGroup]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return muscleGroup.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExerciseList", for: indexPath) as! ExcerciseList
let muscle = muscleGroup[indexPath.row]
cell.textLabel!.text = muscle.excerciseName
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(self.muscleGroup[indexPath.row])
tableView.deselectRow(at: indexPath, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
getJson()
}
func getJson(){
guard let url = URL(string: "https://jsonurl") 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 muscles = try JSONDecoder().decode([MuscleGroup].self, from: data)
for muscle in muscles {
let muscleGroup = muscle.excerciseName
print(muscleGroup)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
}.resume()
}
If I change the var muscleGroup = String to ["Chest", "Back", "Abdominals","Arms", "Legs"] it returns correctly.
Also, the print result on the console returns all the data that needs to be on the Table View.
What am I doing wrong?
As you probably want to use the entire struct
Replace
var muscleGroup = [String]()
with
var muscleGroups = [MuscleGroup]()
Replace
let muscles = try JSONDecoder().decode([MuscleGroup].self, from: data)
for muscle in muscles {
let muscleGroup = muscle.excerciseName
print(muscleGroup)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
with
self.muscleGroups = try JSONDecoder().decode([MuscleGroup].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
Replace
cell.textLabel?.text = self.muscleGroup[indexPath.row]
with
cell.textLabel?.text = self.muscleGroups[indexPath.row].muscleGroup
In your getJson function this line
let muscleGroup = muscle.excerciseName
is creating a new local variable called muscleGroup, change the line to be
self.muscleGroup.append(muscle.excerciseName)
i.e. get rid of the let and append the value to the main array variable
Also move the
DispatchQueue.main.async {
self.tableView.reloadData()
}
to be outside of the for loop of muscles as you are forcing the table to reload for each entry rather than when you are finished
var pokemons = [Pokemon]()
func loadJSON() {
let url = "https://pokeapi.co/api/v2/pokemon/"
guard let urlObj = URL(string: url) else { return }
URLSession.shared.dataTask(with: urlObj) {(data, response, error) in
guard let data = data else { return }
do {
let pokedex = try JSONDecoder().decode(Pokedex.self, from: data)
for pokemon in pokedex.results {
guard let jsonURL = pokemon.url else { return }
guard let newURL = URL(string: jsonURL) else { return }
URLSession.shared.dataTask(with: newURL) {(data, response, error) in
guard let data = data else { return }
do {
let load = try JSONDecoder().decode(Pokemon.self, from: data)
self.pokemons.append(load)
} catch let jsonErr {
print("Error serializing inner JSON:", jsonErr)
}
}.resume()
}
} catch let jsonErr{
print("Error serializing JSON: ", jsonErr)
}
}.resume()
}
This code compiles and runs fine, but the array pokemons returns a count of 0 when I check its size afterwards. It's strange because if I loop through the array right after the pokemons.append(load), it'll spit out a bunch of data. It's as if the data gets lost as soon as it's out of scope of the JSON. Does my array need to be passed by reference or something? I'm looking to take the data and put it into a UITableView, but so far nothing is showing.
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Pokemon"
navigationController?.navigationBar.prefersLargeTitles = true
loadJSON()
print(pokemons.count) //Prints 0
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel()
label.text = "By Id"
label.backgroundColor = UIColor.lightGray
return label
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pokemons.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let name = pokemons[indexPath.row].name
cell.textLabel?.text = "\(name) Section:\(indexPath.section) Row:\(indexPath.row)"
return cell
}
You either need
self.pokemons.append(load)
DispatchQueue.main.async {
self.tableView.reloadData()
}
or write a completion
the asynchronous call is something that doesn't go in serial with function written code , but runs asynchronously until it finishes then it notifies it's callback and in your code the asynchronous method is URLSession.shared.dataTask which runs in a background thread (that's why you have to insert DispatchQueue.main.async inside it to refresh the UI ) and leave the serial execution at main thread for sure you can block the main thread until response returns with a method like Data(contentsOf:url) instead of URLSession.shared.dataTask but it's not a good idea
I want to show data in table cells
now I want show data from api to table cell view on the screen
The output of this api link which is showing in console:
this is struct variable
struct Team: Codable{ //here is struct veriables
var api_id: Int
var id: Int
var first_team:Int
var second_team:Int
var date: String }
here is Im getting data from api
guard let url = URL(string: "http://127.0.0.1:8000/api/matches") 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 {
//here dataResponse received from a network request
let decoder = JSONDecoder()
let model = try decoder.decode([Team].self, from:
dataResponse) //Decode JSON Response Data
print(model)
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
here is want to use data and show on screen
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TeamCell", for: indexPath) as! TeamTableViewCell
//I want to show here data
return cell
}
Create a global variable for storing teams
var teams = [Team]()
Replace
let model = try decoder.decode([Team].self, from:
dataResponse) //Decode JSON Response Data
with
teams = try decoder.decode([Team].self, from:
dataResponse) //Decode JSON Response Data
tableView.reloadData()
And then,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return teams.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TeamCell", for:
indexPath) as! TeamTableViewCell
if let team = teams[indexPath.row] {
cell.firstTeamLabel.text = "\(team.first_team)"
cell.dateLabel.text = team.date
...
// Note: Replace label names with your actual one.
}
return cell
}
I have been working on a launch database for SpaceX and I have successfully parsed my data but the function to create and add the data to the cell is not working. I have added the delegates and data sources but I still cannot find out why it won't run.
import UIKit
struct launchData : Decodable
{
let flight_number : Int
let launch_date_utc : String
struct rocketInfo : Decodable
{
let rocket_name : String
}
let rocket : rocketInfo
}
class LaunchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var launchTableView: UITableView!
var arrayOfLaunchData:[launchData] = []
override func viewDidLoad()
{
super.viewDidLoad()
self.launchTableView.delegate = self
self.launchTableView.dataSource = self
getJsonData()
self.launchTableView.reloadData()
}
func getJsonData()
{
let jsonUrlString = "https://api.spacexdata.com/v2/launches"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let launchDataDecoded = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
} catch let jsonErr {
print("Error Serialization json:", jsonErr )
}
}.resume()
print("getJsonData ran")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("")
print(arrayOfLaunchData.count)
print("")
print("TableView number of rows ran")
return arrayOfLaunchData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellID")
let launch = self.arrayOfLaunchData[indexPath.row]
let flightNumber = launch.flight_number
let rocketName = launch.rocket.rocket_name
cell?.textLabel?.text = "Mission " + String(flightNumber)
let launchDate = launch.launch_date_utc
cell!.detailTextLabel!.text = "Launch Date: " + launchDate + "Rocket Used: " + rocketName
self.launchTableView.reloadData()
print("TableView cellForRowAt ran")
return cell!
}
}
First of all never call reloadData() in cellForRowAt! Delete the line
Two major issues:
reloadData() is called too soon.
The data source array is not populated after receiving the data.
The solution is to delete the line
self.launchTableView.reloadData()
(also) in viewDidLoad() and change getJsonData() to
func getJsonData()
{
let jsonUrlString = "https://api.spacexdata.com/v2/launches"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
self.arrayOfLaunchData = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
DispatchQueue.main.async {
self.launchTableView.reloadData()
}
} catch {
print("Error Serialization json:", error )
}
}.resume()
print("getJsonData ran")
}
because dataTask works asynchronously.
Note:
Please conform to the naming convention that struct and class names start with a capital letter (LaunchData, RocketInfo) and all names are supposed to be camelCased rather than snake_cased.
Remove self.launchTableView.reloadData() from viewDidLoad()
and put on getting successfully data
do {
let launchDataDecoded = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
self.launchTableView.reloadData()
} catch let jsonErr {
print("Error Serialization json:", jsonErr )
}
}.resume()
getJsonData() is follow asynchronous. hope this help!