I want to create the TableView from the data from JSON Data but when I tried to take the value it is always return nil at the second print. Could you please explain and give me some advise about this problem? I already tried hard before asking this question but i'm really new to this:(
Best Regards,
import UIKit
struct News: Decodable {
var status:String
var totalResults:Int
var articles:[Articles]?
}
struct Articles: Decodable{
var source:Source?
var title:String?
var description:String?
var urlToImage:String?
var publishedAt:String?
}
struct Source: Decodable{
var name:String?
}
class NewsViewController: UIViewController{
var a = [String]()
@IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//Hit API
let url = URL(string: "http://newsapi.org/v2/top-headlines?country=th&category=technology&apiKey=2b27ab9b590041a6a6dcdf4ef94a0a33")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
let result = try JSONDecoder().decode(News.self,from: data!)
let totalresult = result.articles!.count
for result in result.articles!{
let titleUnWrapped: String = result.title ?? ""
let urlToImageUnWrapped: String = result.urlToImage ?? ""
self.a.append(titleUnWrapped)
// print(self.a) <<<<<<<<<<<<<<<<<<<<<<<<< Work
}
} catch {
print("ERROR")
}
}
}.resume()
print(self.a) //Return nil <<<<<<<<<<<<<<<<<<<<<<<<< Not Work
}
}
I think the problem is just a lack of experience with closures. This part of the line: URLSession.shared.dataTask(with: url!)
sends a request to your url (ie some remote server somewhere) but has no real idea exactly how long it will take to get a response. Instead of waiting and blocking the main thread (unresponsive UI and bad user experience) it makes its request outside the main thread and your code continues to execute while it waits. That is why the last print statement fails but it means that the user can still interact with your app by scrolling a table or whatever.
BUT when the response is eventually received from the remote data source it is passed into your closure and the code inside is executed. So at this point you either have success (data is good) or some failure (check response and error). If you have good data then you can pass it on to some external function to continue working with it. eg
class NewsViewController: UIViewController{
var a = [String]()
@IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//Hit API
let url = URL(string: "http://newsapi.org/v2/top-headlines?country=th&category=technology&apiKey=2b27ab9b590041a6a6dcdf4ef94a0a33")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
let result = try JSONDecoder().decode(News.self,from: data!)
let totalresult = result.articles!.count
for result in result.articles! {
let titleUnWrapped: String = result.title ?? ""
let urlToImageUnWrapped: String = result.urlToImage ?? ""
self.a.append(titleUnWrapped)
// print(self.a) <<<<<<<<<<<<<<<<<<<<<<<<< Work
self.doSomethingWithResult(with: a) //<<<< Pass `a` out of the closure
}
} catch {
print("ERROR")
}
} else {
// Error is not nil so do something...
print("ERROR: \(error)")
}.resume()
}
// This method is called from inside the closure only when you have good
// data returned from your api call.
func doSomethingWithResult(with goodData: [String]) {
// Use `a`...
}
}
So you don't have to worry about trying to access the data too early or waiting for it to finish, the closure won't be called until you have a complete response, either a good one or a bad one. Hope this helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.