根本问题是您正在异步检索数据(例如,getUsers 将使用URLSession 从网络发起一个相对较慢的请求,但会立即返回)。因此这是行不通的:
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
print(news)
}
在检索到 news 之前,您正在从 getUsers 返回。所以news 仍然是[]。
解决方案是给getUsers一个“completion handler”,一个参数,你可以在其中指定异步请求完成时应该执行什么代码:
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)
}