Json data not showing on tableView swift 5 - ios

I fetched the data and it is showing when printing but when i try to display it on tableview.Nothing is coming
Am i placing tableview.reloadData in wrong place ?
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
tableView.reloadData()
}
func fetchData()
{
if let url = URL(string: urlConstant) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, res, err) in
if err == nil
{
let decoder = JSONDecoder()
if let safeData = data
{
do{
let results = try decoder.decode(Results.self, from: safeData)
guard let array = results.Result as? [Products] else {return }
for product in array
{
self.productArray.append(product)
}
} catch {
print(error)
}
}
}
}
task.resume()
}
self.tableView.reloadData()
}

If you are getting correct response(check it once) from the server then next thing you need to reload tableView after getting the response from the server and populating the array.
func fetchData() {
if let url = URL(string: urlConstant) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, res, err) in
if err == nil
{
let decoder = JSONDecoder()
if let safeData = data
{
do{
let results = try decoder.decode(Results.self, from: safeData)
guard let array = results.Result as? [Products] else {return }
for product in array {
self.productArray.append(product)
}
// Reload table view here
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
}
task.resume()
}
}
Alternative, you can add completion handled in fetchData method.

Related

DispatchGroup Order of Operation

Hello i am trying to get result based on a result from another api.The getLatest function is going to get the latest object and will return the number then i want to generate a list of urls with the latest number and then sequentially get the data in the function getXKCDData.How can i achieve this.The dispatchgroup i have on there works sometimes but doesnt work all the time because generateUrlList is called before getting the latest number and it generate the wrong url.Which causes The operation couldn’t be completed. (XKCD_Comics.NetworkError error 1.) error.How can i achieve this.
final class NetworkManager {
public static var shared = NetworkManager()
private let sessions = URLSession.shared
private func generateUrlList(latestXKCD: Int) -> [String] {
print("Here at generateUrlList")
var urls = [String]()
for i in latestXKCD-100...latestXKCD{
let xkcd_url = "https://xkcd.com/\(i)/info.0.json"
urls.append(xkcd_url)
}
return urls
}
private func getLatest(completion: #escaping(Int) -> ()){
print("Here at get latest")
guard let url = URL(string: "https://xkcd.com/info.0.json") else { return }
let task = sessions.dataTask(with: url) { data, response, error in
guard let jsonData = data else { return }
do {
let decoder = JSONDecoder()
let response = try decoder.decode(Comics.self, from: jsonData)
completion(response.num)
}catch {
completion(0)
}
}
task.resume()
}
public func getXKCDData(completion: #escaping(Result<Comics,NetworkError>) -> Void){
let group = DispatchGroup()
var latestID = 0
group.enter()
self.getLatest { (result) in
latestID = result
group.leave()
}
let urlList = generateUrlList(latestXKCD: latestID)
print(urlList)
print("Here at getXKCDData")
for url in urlList {
group.enter()
guard let url = URL(string: url) else {
completion(.failure(.InvalidURL))
return
}
let task = sessions.dataTask(with: url) { data, response, error in
guard let jsonData = data else {
completion(.failure(.NoDataAvailable))
return
}
do {
let decoder = JSONDecoder()
let response = try decoder.decode(Comics.self, from: jsonData)
completion(.success(response))
group.leave()
}catch {
completion(.failure(.CanNotProcessData))
}
}
task.resume()
}
}
}

How to get an array from URLSession

Trying to make a program for a news site. I take information from the site through the api, everything works fine.
The only question is, how do I get this array out of the loop?
Here is my code:
import UIKit
class ViewController: UIViewController {
var news:[News] = []
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
print(news)
}
func getUsers() {
guard let url = URL(string: "http://prostir.news/swift/api2.php") else {return}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
news = try JSONDecoder().decode([News].self, from: data)
// print(self.news)
} catch let error {
print(error)
}
}
}.resume()
}
}
struct News:Codable, CustomStringConvertible{
let href:String?
let site:String?
let title:String?
let time:String?
var description: String {
return "(href:- \(href), site:- \(site), title:- \(title), time:- \(time))"
}
}
Declare news array in your class and assign the response to this array in getUsers method
var news:[News] = []
func getUsers(){
guard let url = URL(string: "https") else {return}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
self.news = try JSONDecoder().decode([News].self, from: data)
print(self.news)
} catch let error {
print(error)
}
}
}.resume()
}
The fundamental problem is you are retrieving data asynchronously (e.g. getUsers will initiate a relatively slow request from the network using URLSession, but returns immediately). Thus this won’t work:
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
print(news)
}
You are returning from getUsers before the news has been retrieved. So news will still be [].
The solution is to give getUsers a “completion handler”, a parameter where you can specify what code should be performed when the asynchronous request is done:
enum NewsError: Error {
case invalidURL
case invalidResponse(URLResponse?)
}
func getUsers(completion: #escaping (Result<[News], Error>) -> Void) {
let queue = DispatchQueue.main
guard let url = URL(string: "http://prostir.news/swift/api2.php") else {
queue.async { completion(.failure(NewsError.invalidURL)) }
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
queue.async { completion(.failure(error)) }
return
}
guard
let data = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
queue.async { completion(.failure(NewsError.invalidResponse(response))) }
return
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let news = try decoder.decode([News].self, from: data)
queue.async { completion(.success(news)) }
} catch let parseError {
queue.async { completion(.failure(parseError)) }
}
}.resume()
}
Then your view controller can fetch the news, passing a “closure”, i.e. code that says what to do when the asynchronous call is complete. In this case, it will set self.news and trigger the necessary UI update (e.g. maybe refresh tableview):
class ViewController: UIViewController {
var news: [News] = []
override func viewDidLoad() {
super.viewDidLoad()
fetchNews()
}
func fetchNews() {
getUsers() { result in
switch result {
case .failure(let error):
print(error)
case .success(let news):
self.news = news
print(news)
}
// trigger whatever UI update you want here, e.g., if using a table view:
//
// self.tableView.reloadData()
}
// but don't try to print the news here, as it hasn't been retrieved yet
// print(news)
}

How to assign Images url (dynamical ) to particular emp id's image view in swift

I have a UITableviewCell in that cell I have added name, emp_id, and UIImageview to display the data I have 2 url's in which one url has displaying names and emp_id's and another Url have images along emp_id's(same emp_id's ) and I have to show that images to there names using help of emp_id's. I am able to show details but not able to implement the images here is my code
struct jsonstruct5:Decodable {
var name:String
var emp_id:String
var url:String?
}
struct jsonstruct21:Decodable {
var url:String?
var emp_id:String
}
var arrdata = [jsonstruct5]()
var arrdata1 = [jsonstruct21]()
func getdata(){
let url = URL(string: "https://sp/company/employees_detail/app")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{if error == nil{
self.arrdata = try JSONDecoder().decode([jsonstruct5].self, from: data!)
for mainarr in self.arrdata{
// print(data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}catch{
print("Error in get json data")
}
}.resume()
}
Response is
[
{
"name":"Sonu",
"emp_id":"01"
},
{
"name":"Prasanth",
"emp_id":"02"
},
{
"name":"Patra",
"emp_id":"03"
}.
]
func getdata1(){
let url = URL(string: "https://sp/company/employees_detail/profile/photos")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{if error == nil{
self.arrdata1 = try JSONDecoder().decode([jsonstruct21].self, from: data!)
for mainarr1 in self.arrdata1{
// print(mainarr.name,":",mainarr.dob)
print(data)
print(mainarr1.url)
let data1 = try? Data(contentsOf: url!)
print(data1)
if let imageData = data {
let image4 = UIImage(data: imageData)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}catch{
print("Error in get json data")
print(error)
}
}.resume()
}
Response is
[
{
"url":"https//ps/Image2",
"emp_id":"01"
},
{
"url":null,
"emp_id":"02"
},
{
"url":"https//ps/Image4",
"emp_id":"03"
}
]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:AppreTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! AppreTableViewCell
cell.nameLbl.text = "\(arrdata[indexPath.row].name)"
cell.dateLbl.text = "\(arrdata[indexPath.row].emp_id)"
print(DataManager.sharedInstance.empID)
if (arrdata[indexPath.row].emp_id == DataManager.sharedInstance.empID)
{
cell.isHidden=true
}
else{
cell.isHidden=false
}
// tableView.alwaysBounceVertical = false
return cell
}
Try Below
struct jsonstruct5:Decodable {
var name:String
var emp_id:String
var url:String?
}
struct jsonstruct21:Decodable {
var url:String?
var emp_id:String
}
var arrdata = [jsonstruct5]()
var arrdata1 = [jsonstruct21]()
func getdata(){
let url = URL(string: "https://sp/company/employees_detail/app")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do { if error == nil {
self.arrdata = try JSONDecoder().decode([jsonstruct5].self, from: data!)
self. getdata1()
// don't for put reloadData in for loops, always play with your data/model and after loop you can call reloadData method.
}catch{
print("Error in get json data")
}
}.resume()
}
func getdata1(){
let url = URL(string: "https://sp/company/employees_detail/profile/photos")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do{if error == nil{
self.arrdata1 = try JSONDecoder().decode([jsonstruct21].self, from: data!)
// I don't know your model type, but you can put "url" from each iteration of second array (arrdata1) into first array (arrdata) where employee id matches.
// don't download images in below loop, rather download them in cellForRowAt method.
// then you just call self.tableView.reloadData()
// update your model
for item1 in arrdata1 {
if let index = arrdata.index(where: {$0["emp_id"] == item1["emp_id"]}) {
var item = arrdata[index]
item["url"] = item1["url"]
arrdata[index] = item
}
}
// or below code
for item1 in arrdata1 {
if let index = arrdata.index(where: {$0.emp_id == item1.emp_id}) {
var item = arrdata[index]
item.url = item1.url
arrdata[index] = item
}
}
// model array has been updated, now work on cellForRowAt method
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}catch{
print("Error in get json data")
print(error)
}
}.resume()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:AppreTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as! AppreTableViewCell
let model = arrdata[indexPath.row]
cell.nameLbl.text = "\(model.name)"
cell.dateLbl.text = "\(model.emp_id)"
// here you download image for only current model.url
if let url = model.url {
cell.nameOfImageView.loadImageUsingCacheWithURLString(url, placeHolder: UIImage(named: "someImage")!, completionBlock: { (image) in
cell.nameOfImageView.image = image
})
}
// tableView.alwaysBounceVertical = false
return cell
}
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage, completionBlock:#escaping (_ image:UIImage)->()) {
if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
self.image = cachedImage
completionBlock(cachedImage)
}
else if let url = URL(string: URLString) {
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
//print("RESPONSE FROM API: \(response)")
if error != nil {
print("ERROR LOADING IMAGES FROM URL: \(error!.localizedDescription)")
DispatchQueue.main.async {
self.image = placeHolder
completionBlock(placeHolder)
}
}
else {
DispatchQueue.main.async {
if let data = data {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
self.image = downloadedImage
completionBlock(downloadedImage)
}
else {
self.image = placeHolder
completionBlock(placeHolder)
}
}
else {
self.image = placeHolder
completionBlock(placeHolder)
}
}
}
}).resume()
}
else if URLString.isEmpty {
completionBlock(placeHolder)
}
}
}

I have problems with JSON - Data is repeated

I have a app and problem is repeating the data inside tableView.
How do I fix data replication inside Array ?
func jsonGet(page: Int) {
let pathFull = "https://test.com"
guard let url = URL(string: pathFull) else {return} //
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let posts = try JSONDecoder().decode(JobsData.self, from: data)
for post in posts.jobs.data {
self.newsPost.append(post)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let jsonErr {
self.alertViewBaisc(title: "erorr", text: "error", button: "ok")
print("Error serializing json", jsonErr)
}
}.resume()
}
Issue is you are keep inserting in newsPost data see self.newsPost.append(post) Line
Replace your code with
func jsonGet(page: Int) {
let pathFull = "https://test.com"
guard let url = URL(string: pathFull) else {return} //
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let posts = try JSONDecoder().decode(JobsData.self, from: data)
var tempPost = [DataType]()
for post in posts.jobs.data {
tempPost.append(post)
}
self. newsPost = tempPost
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let jsonErr {
self.alertViewBaisc(title: "erorr", text: "error", button: "ok")
print("Error serializing json", jsonErr)
}
}.resume()
}
Also another way is you can check that if post is already in your array or not
(self. newsPost.filter{$0.uniqueKey == post.uniqueKey}.count == 0)
You can clear the arr with every call
self.newsPost.removeAll()
or declare it like
var newsPost = Set<Post>() // this will guarantee uniqueness in a single fetch
also you don't need a for - loop you can directly do
newsPost = posts.jobs.data
apart from the issue
You can use contains to sort out this issue:
let posts = try JSONDecoder().decode(JobsData.self, from: data)
for post in posts.jobs.data {
if self.newPost contains(post){
//Skip
}
else{
self.newsPost.append(post)
}
}
Just replace
for post in posts.jobs.data {
self.newsPost.append(post)
}
with
self.newsPosts = posts.jobs.data
This avoids the redundant repeat loop as well as the duplicate entries since the new data replaces the old.

Trying to append JSON items to array but not working

I am trying to serialize a GET request then make a movie object, then appending that movie object to a movies array which I will use to show info on the UI.
I am new and have struggled with this problem for some time now :(
If you look at the self.movies?.append(movie) shouldnt that work? I dont see any reasons as to when i try to get the first item i get fatal error index out of bounds which means I the Array is not filled yet.... Dont know what i am doing wrong :(
import UIKit
class ViewController: UIViewController {
var movies:[Movie]? = []
#IBOutlet weak var uiMovieTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getMovieData()
print(self.movies?.count)
setUI()
}
#IBAction func yesBtn(_ sender: UIButton) {
print(movies?[5].title ?? String())
}
#IBAction func seenBtn(_ sender: UIButton) {
}
#IBAction func noBtn(_ sender: UIButton) {
}
#IBOutlet weak var moviePoster: UIImageView!
let urlString = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
func getMovieData(){
//Set up URL
let todoEndPoint: String = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
guard let url = URL(string: todoEndPoint) else {
print("Cant get URL")
return
}
let urlRequest = URLRequest(url: url)
//Setting up session
let config = URLSessionConfiguration.default
let session = URLSession.shared
//Task setup
let task = session.dataTask(with: urlRequest) { (data, URLResponse, error) in
//Checking for errors
guard error == nil else{
print("Error calling GET")
print(error)
return
}
//Checking if we got data
guard let responseData = data else{
print("Error: No data")
return
}
self.movies = [Movie]()
do{//If we got data, if not print error
guard let todo = try JSONSerialization.jsonObject(with: responseData, options:.mutableContainers) as? [String:AnyObject] else{
print("Error trying to convert data to JSON")
return
}//if data is Serializable, do this
if let movieResults = todo["results"] as? [[String: AnyObject]]{
//For each movieobject inside of movieresult try to make a movie object
for moviesFromJson in movieResults{
let movie = Movie()
//If all this works, set variables
if let title = moviesFromJson["title"] as? String, let movieRelease = moviesFromJson["release_date"] as? String, let posterPath = moviesFromJson["poster_path"] as? String, let movieId = moviesFromJson["id"] as? Int{
movie.title = title
movie.movieRelease = movieRelease
movie.posterPath = posterPath
movie.movieId = movieId
}
self.movies?.append(movie)
}
}
}//do end
catch{
print(error)
}
}
////Do Stuff
task.resume()
}
func setUI(){
//uiMovieTitle.text = self.movies![0].title
//print(self.movies?[0].title)
}
}
my Movie class:
import UIKit
class Movie: NSObject {
var title:String?
var movieRelease: String?
var posterPath:String?
var movieId:Int?
var movieGenre:[Int] = []
//public init(title:String, movieRelease:String, posterPath:String,movieId:Int) {
// self.movieId = movieId
//self.title = title
//self.movieRelease = movieRelease
//self.posterPath = posterPath
//self.movieGenre = [movieGenre]
//}
}
getMovieData calls the network asynchronously. Your viewDidLoad invokes this, then calls setUI() - but the networking is still ongoing when setUI is called.
Instead, call setUI when the networking is complete - after the self.movies?.append(movie) line. The UI code will need to happen on the main thread. So...
for moviesFromJson... // your existing code
...
self.movies?.append(movie)
}
// Refresh UI now movies have loaded.
DispatchQueue.main.async {
setUI()
}
import UIKit
class ViewController: UIViewController {
var movies:[Movie]? = []
#IBOutlet weak var uiMovieTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getMovieDataCall(completionHandler: {data, error in self. getMovieDataCallBack(data: data, error: error)})
}
func getMovieDataCallBack(data: Data?, error: Error?) {
if error == nil {
let dictionary = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! Dictionary<String, AnyObject>
//do your appending here and then call setUI()
print("dictionaryMovie \(dictionary)")
} else {
showAlertView("", error?.localizedDescription)
}
}
func getMovieDataCall(completionHandler: #escaping (Data?, Error?) -> Void)){
//Set up URL
let todoEndPoint: String = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
guard let url = URL(string: todoEndPoint) else {
print("Cant get URL")
return
}
let urlRequest = URLRequest(url: url)
//Setting up session
let config = URLSessionConfiguration.default
let session = URLSession.shared
//Task setup
let task = session.dataTask(with: urlRequest) { (data, URLResponse, error) in
if error != nil {
NSLog("GET-ERROR", "=\(error)");
completionHandler(nil, error)
} else {
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
print(dataString!)
completionHandler(data, nil)
}
task.resume()
}
func setUI(){
}

Resources