简体   繁体   English

如何从 URLSession 获取数组

[英]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.我通过 api 从站点获取信息,一切正常。

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()
    }
}

There is no loop, you have to add a loop 没有循环,您必须添加一个循环

struct News{
   let href: String
   let site: String
   let time: String
}

var newsData = [News]()

...

do {
    guard let jsonArray = try JSONSerialization.jsonObject(with: dataResponse) as? [[String: Any]] else { return }
    for item in jsonArray {
        if let href = item["href"] as? String,
            let site = item["site"] as? String,
            let time = item["time"] as? String {

            self.newsData.append(News(href: href, site: site, time:time))
        }
    }
} catch {
    print("Error", error)
}
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在您的类中声明新闻数组并在 getUsers 方法中将响应分配给该数组

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 (eg getUsers will initiate a relatively slow request from the network using URLSession , but returns immediately).根本问题是您正在异步检索数据(例如, getUsers将使用URLSession从网络发起一个相对较慢的请求,但会立即返回)。 Thus this won't work:因此这行不通:

override func viewDidLoad() {
    super.viewDidLoad()
    getUsers()

    print(news)
}

You are returning from getUsers before the news has been retrieved.您正在检索news之前从getUsers返回。 So news will still be [] .所以news仍然是[]

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:解决方案是给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()
}

Then your view controller can fetch the news, passing a “closure”, ie 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 (eg maybe refresh tableview):在这种情况下,它将设置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)
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM