根本问题是您正在异步检索数据(例如getUsers
,将使用 发起来自网络的相对较慢的请求URLSession
,但立即返回)。因此这不起作用:
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
print(news)
}
您正在从被检索getUsers
之前返回。news
所以news
还是会[]
的。
解决方案是提供getUsers
一个“完成处理程序”,一个参数,您可以在其中指定异步请求完成时应执行的代码:
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()
}
然后您的视图控制器可以获取消息,传递一个“闭包”,即说明异步调用完成时要做什么的代码。在这种情况下,它将设置self.news
并触发必要的 UI 更新(例如,可能刷新 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)
}