Swift 4 - URLSession

Swift 4 - URLSession

我在从网站解析 API 时遇到了一个小问题 www.adx.ae

这是两个 API:

1- 所有 public 公司的列表:https://www.adx.ae/en/_vti_bin/adx/svc/Members.svc/ListedCompanies?showPrivate=false&showPublic=true

2- 每个公司的详细信息(按 ID): https://www.adx.ae/en/_vti_bin/ADX/svc/trading.svc/ListedCompanyOrderBook?listedCompanyID=ADIB

我们只需要根据每家公司的ID去碰巧得到其最新的交易价格……等等

我的目标是在 tableView 中列出每个公司的名称和交易价格。

到目前为止,这是我所做的

import UIKit

class ViewController: UITableViewController {

    func getPrice(company: [Company]) {

        var price = [String]()
        let companyLink = "https://www.adx.ae/en/_vti_bin/ADX/svc/trading.svc/ListedCompanyOrderBook?listedCompanyID="

        for comp in company {
            let link = companyLink + comp.ID

            guard let url = URL(string: link) else { return }

            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

                guard let dataResult = data else { return }

                do {

                    let company = try JSONDecoder().decode(SpecificCompany.self, from: dataResult)

                    DispatchQueue.main.async {
                        price.append(String(company.LastTradePrice))
                        self.tableView.reloadData()

                    }

                } catch {
                    print(error)
                }
            }).resume()
        }

        print(price)
        allPrices = price

    }

    struct Company: Decodable {
        let Name: String
        let ID: String
        var link: String {
            get {
                return "https://www.adx.ae/en/_vti_bin/ADX/svc/trading.svc/ListedCompanyOrderBook?listedCompanyID=" + ID
                }
        }

    }

    struct SpecificCompany: Decodable {
        let LastTradePrice: Double
    }

    var allCompanies = [Company]()
    var allPrices = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        getCompanies()
    }

    func getCompanies() {

        let link = "https://www.adx.ae/en/_vti_bin/adx/svc/Members.svc/ListedCompanies?showPrivate=false&showPublic=true"

        guard let url = URL(string: link) else { return print("Error") }

        URLSession.shared.dataTask(with: url) { (data, response, error) in

            guard let obtainedData = data else { return print("No data")}

            do {
                let adx = try JSONDecoder().decode([Company].self, from: obtainedData)

                DispatchQueue.main.async {
                    self.allCompanies = adx
                    self.tableView.reloadData()
                }

            } catch {
                print("Nothing")
                print(error)
            }

            }.resume()

    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return allCompanies.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = allCompanies[indexPath.row].ID
        cell.detailTextLabel?.text = allPrices[indexPath.row]
        return cell
    }
}

我无法获得我正在寻找的数组中的价格。在调用 cellForRowAt indexPath 函数之前,allCompanies 数组似乎是空的!!即使将函数 getPrice 放在那里也不能解决问题。

我是不是遗漏了什么?

当您创建控制器时 class,它会查看 allCompanies 数组以获取 UITableView 有多少行,并且因为它是空的(不是 0),所以它崩溃了。使用以下代码更改 "numberOfRowsInSection"。

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if(allCompanies.count == 0)
    {
        return 0
    }else{
        return allCompanies.count
    }
    }

为了获取价格,您将 allCompanies 数组发送到 fetchPrice 函数,但您是在它为空时执行此操作。您可以在获取公司后调用 fetchPrice 函数。此外,print(prices) 打印空数组,因为网络请求是异步的,但您无需等待回答即可打印。

  func getCompanies() {
    let link = "https://www.adx.ae/en/_vti_bin/adx/svc/Members.svc/ListedCompanies?showPrivate=false&showPublic=true"
    guard let url = URL(string: link) else { return print("Error") }
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let obtainedData = data else { return print("No data")}
        do {
            let adx = try JSONDecoder().decode([Company].self, from: obtainedData)
            DispatchQueue.main.async {
                self.allCompanies = adx
                self.tableView.reloadData()
                self.getPrice(company: self.allCompanies)
            }
        } catch {
            print("Nothing")
            print(error)
        }
        }.resume()
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.textLabel?.text = allCompanies[indexPath.row].ID
    if(allPrices.count != 0 && allPrices.count > indexPath.row){
        cell.textLabel?.text = allPrices[indexPath.row]
    }
    return cell
}

编辑 2:每次请求完成并刷新表视图时,我都会将字符串附加到价格数组。现在它覆盖了单元格的常规文本。但是,您可以将其写在任何您想要的地方。这将是比等待所有请求完成更好的方法。

获取价格函数;

var allPrices = [String]()

func getPrice(company: [Company]) {

let companyLink = "https://www.adx.ae/en/_vti_bin/ADX/svc/trading.svc/ListedCompanyOrderBook?listedCompanyID="
for comp in company {
    let link = companyLink + comp.ID
    guard let url = URL(string: link) else { return }
    URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

        guard let dataResult = data else { return }

        do {

            let company = try JSONDecoder().decode(SpecificCompany.self, from: dataResult)

            DispatchQueue.main.async {
                self.allPrices.append(String(company.LastTradePrice))
                self.tableView.reloadData()
                print(self.allPrices)
            }
        } catch {
            print(error)
        }
    }).resume()
}
self.tableView.reloadData()
}

此外; 这段代码可以工作,但我建议你为这种操作编写一个网络层并将它们从你的控制器中移出 classes.