Swift: проблема многопоточности Получение данных из API - PullRequest
0 голосов
/ 15 февраля 2020

У меня проблема с тем, что иногда я не могу успешно декодировать файл json из-за медленного сетевого вызова. У меня есть список акций, и я пытаюсь успешно расшифровать каждую из них до того, как будет декодирована следующая акция.

Например, у меня будет список из 4 акций, но 3 будет успешно декодирован, а 1 - нет. Тот, который терпит неудачу, также случайен. Когда я распечатываю URL для того, который терпит неудачу, файл url и json правильный, но я получаю ошибку, что он не читает, потому что он в неправильном формате.

Список акций получен через Firebase и после их получения у меня есть обработчик завершения, который пытается выполнить сетевой вызов на сервер. Причина, по которой я добавил код Firestore, заключается в том, что, когда я ставлю точку остановки в том случае, если дело успешно, я замечаю, что он попадает в поток 5 из 14 потоков. Имеет ли это много общих тем? Я знаю, что это проблема многопоточности, но у меня такая огромная проблема с определением того, где я должен делать dispatchGroups. Буду признателен за любую помощь и разъяснения!

APIManager

private var stocks = [Stock]()

func getStockList( for symbols: [Stock], completion: ((Result<[Stock]>) -> Void)?) {
    let dispatchGroup = DispatchGroup()

    for symbol in symbols {
        var urlComponents = URLComponents()
        urlComponents.scheme = "https"
        urlComponents.host = APIManager.baseAPIURL
        urlComponents.path = "\(APIManager.baseRelativePath)/market/batch"

        //URL parameters
        let token = URLQueryItem(name: "token", value: "fakeToken")
        let symbolsItem = URLQueryItem(name: "symbols", value: symbol.symbol)

        let typesItem = URLQueryItem(name: "types", value: "quote")

        urlComponents.queryItems = [token, symbolsItem, typesItem]
        guard let url = urlComponents.url else { fatalError("Could not create URL from components") }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        dispatchGroup.enter()
            let task = session.dataTask(with: request) { (responseData, response, err) in
                    guard err == nil else {
                        completion?(.failure(err!))
                        return
                    }

                    guard let jsonData = responseData else {
                        let err = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
                        completion?(.failure(err))
                        return
                    }

                    let decoder = JSONDecoder()
                    do {
                        let data = try decoder.decode([String: Stock].self, from: jsonData)
                        let jsonData = data.map{ $0.value }
                        completion?(.success(jsonData))

                        dispatchGroup.leave()

                    } catch {
                        completion?(.failure(error))
                        print("Failed to decode using stock URL for \(symbol.symbol ?? ""): \n \(url)")
                    }
                }
            task.resume()
    }
}

HomeViewController

 class HomeViewController: UIViewController, LoginViewControllerDelegate {
override func viewDidLoad() {
    super.viewDidLoad()

    fetchStocksFromFireStore(completion: { (stocks) -> Void in
        APIManager.shareInstance.getStockList(for: self.fetchedStocks) { (result) in

            switch result {
            case .success(let stocks):
                stocks.forEach { (stock) in
                    print("Company: \(stock.companyName ?? "")")
                }

            case .failure(let error):
                print(error.localizedDescription)
            }
        }
        self.tableView.reloadData()

    })
  }
}

func fetchStocksFromFireStore(completion: @escaping ([Stock]) -> ()) {
    let uid = Auth.auth().currentUser?.uid ?? ""
    let db = Firestore.firestore()

    db.collection("users").document(uid).collection("stocks").getDocuments { (snapshot, err) in
        if let err = err {
            print("Error getting stocks snapshot", err.localizedDescription)
            return

        } else {
            snapshot?.documents.forEach({ (snapshot) in
                var stock = Stock()
                stock.symbol = snapshot.documentID
                self.fetchedStocks.append(stock)
            })

            completion(self.fetchedStocks)

        }
    }
}

Модель

struct Stock {
var symbol: String?
var companyName: String?
var latestPrice: Double?

enum CodingKeys: String, CodingKey {
    case symbol
    case companyName
    case latestPrice
}

enum QuoteKeys: String, CodingKey {
    case quote
 }
}

extension Stock: Encodable {
func encode(to encoder: Encoder) throws {
    var quoteContainer = encoder.container(keyedBy: QuoteKeys.self)
    var quoteNestedValues = quoteContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .quote)
    try quoteNestedValues.encode(symbol, forKey: .symbol)
    try quoteNestedValues.encode(companyName, forKey: .companyName)
    try quoteNestedValues.encode(latestPrice, forKey: .latestPrice)
 }
}

extension Stock: Decodable {
init(from decoder: Decoder) throws {
    let quoteValues = try decoder.container(keyedBy: QuoteKeys.self)
    let quoteNestedValues = try quoteValues.nestedContainer(keyedBy: CodingKeys.self, forKey: .quote)

    symbol = try quoteNestedValues.decode(String.self, forKey: .symbol)
    companyName = try quoteNestedValues.decode(String.self, forKey: .companyName)
    latestPrice = try quoteNestedValues.decode(Double.self, forKey: .latestPrice)
 }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...