При обновлении пользовательского интерфейса с ответом json: «Поток 1: неустранимая ошибка: индекс выходит за пределы диапазона».получено - Swift - PullRequest
0 голосов
/ 02 июня 2018

Попытка обновить пункт меню, чтобы вернуть все приборы из API.

У меня есть список возвращаемых приборов.

Как мне выполнить обновление fixtureMenuItem в MenuControllerсо всеми приборами, возвращенными из JSON?Я думал, что смогу сделать что-то вроде fixtureMenuItem.title = fixtures.description, но я получаю «Поток 1: Неустранимая ошибка: индекс выходит за пределы диапазона».

Модель

struct LiveScores: Codable {
    let success: Bool
    let fixturesData: FixturesData?
    enum CodingKeys: String, CodingKey {
        case fixturesData = "data"
        case success
    }
}

struct FixturesData: Codable {
    let fixtures: [Fixture]
    let nextPage, prevPage: Bool

    enum CodingKeys: String, CodingKey {
        case fixtures
        case nextPage = "next_page"
        case prevPage = "prev_page"
    }
}

struct Fixture: Codable, CustomStringConvertible {
    let id, date, time, round: String
    let homeName, awayName, location, leagueID: String
    let homeID, awayID: Int?

enum CodingKeys: String, CodingKey {
    case id, date, time, round
    case homeName = "home_name"
    case awayName = "away_name"
    case location
    case leagueID = "league_id"
    case homeID = "home_id"
    case awayID = "away_id"
}

var description: String {
    return "\(time): \(homeName) vs. \(awayName)"
    }
}

// MARK: Convenience initializers

extension LiveScores {
    init(data: Data) throws {
        self = try JSONDecoder().decode(LiveScores.self, from: data)
    }
}

Контроллер меню - здесь я хочу обновить пункт меню прибора, включив время, дом иназвания команд на выезде«Вот где все светильники будут заселены!»- это жестко закодированный текст, который я хочу заменить данными прибора.

var fixtures = [Fixture]()

func updateScores() {
    liveScoreApi.fetchFixtures()
    if let fixtureMenuItem = self.Menu.item(withTitle: "Fixtures") {
        fixtureMenuItem.title = "Here is where all the fixtures will be populated!"
        // TODO - populate the UI with fixtures returned from JSON response
    }
}  

Fetch Fixtures - здесь, где извлекаются приборы.

func fetchFixtures() {
    let session = URLSession.shared
    let url = URL(string: "\(baseUrl)fixtures/matches.json?key=\ 
(apiKey)&secret=\(apiSecret)&date=2018-06-02")
    let task = session.dataTask(with: url!) { data, response, err in
        // check for a hard error
        if let error = err {
            NSLog("Live Scores Api Error: \(error)")
        }

        // check the response code
        if let httpResponse = response as? HTTPURLResponse {
            switch httpResponse.statusCode {
            case 200: // perfecto!
                if let liveScores  = try? LiveScores.init(data: data!),
                    let fixture = liveScores.fixturesData
                    {
                    NSLog("\(fixture)")
                    }
            case 401: // unauthorised
                NSLog("Live Score Api returned an 'unauthorised' response.")
            default:
                NSLog("Live Scores Api returned response: %d %@", httpResponse.statusCode, HTTPURLResponse.localizedString(forStatusCode: httpResponse.statusCode))
            }
        }
    }
    task.resume()
}

InВ этом примере данные о приборах есть 26 приборов, и я хочу показать все из них.

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Как сказал Дункан в своем ответе, проблема заключалась в том, что результаты на самом деле не были доступны.

Я реализовал обработчик завершения handleCompletion: для функции fetchFixtures(), которая принимает значение true / false плюс данные фиксаторов.Затем он возвращается в каждом случае ответа http, как показано ниже:

func fetchFixtures(handleCompletion:@escaping (_ isOK:Bool,_ param: 
    FixturesData?)->()) {
        let session = URLSession.shared
        let url = URL(string: "\(baseUrl)fixtures/matches.json?key=\ 
                     (apiKey)&secret=\(apiSecret)&date=2018-06-04")
        let task = session.dataTask(with: url!) { data, response, err in
        // check for a hard error
        if let error = err {
            NSLog("Live Scores Api Error: \(error)")
        }

        // check the response code
        if let httpResponse = response as? HTTPURLResponse {
            switch httpResponse.statusCode {
            case 200: // perfecto!
                if let liveScores  = try? LiveScores.init(data: data!),
                    let fixture = liveScores.fixturesData
                    {
                    //NSLog("\(fixture)")
                    handleCompletion(true, fixture)
                    }
            case 401: // unauthorised
                NSLog("Live Score Api returned an 'unauthorised' response.")
                handleCompletion(false, nil) 
            default:
                NSLog("Live Scores Api returned response: %d %@", httpResponse.statusCode, HTTPURLResponse.localizedString(forStatusCode: httpResponse.statusCode))
                handleCompletion(false, nil)
            }
        }
    }
    task.resume()
}

После реализации вышеизложенного я реорганизовал updateScores() для использования этого обработчика завершения.

    func updateScores() {
    liveScoreApi.fetchFixtures() { (
        isOK, fixture) in
        if isOK == true {
            if let fixtureMenuItem = self.Menu.item(withTitle: "Fixtures") {
                fixtureMenuItem.title = (fixture?.fixtures.description)!
            }
        }
        else {
            NSLog("error fetching!")
        }
    }
}

fixtureMenuItem теперь успешно отображает данные, если они доступны.

0 голосов
/ 02 июня 2018

Вариации этого вопроса постоянно возникают на SO.

Асинхронные функции не ждут, пока их результаты станут доступны.Вы даете им обратный вызов, который представляет собой замыкание (блок кода, который вы предоставляете), которое выполняется после завершения операции.

Вы должны переписать свою функцию fetchFixtures(), чтобы получить обработчик завершения, а затем выполнить рефакторингваша updateScores() функция для передачи кода, который обновляет ваш пункт меню в обработчик завершения для FetchFixtures.

См. мой ответ на вопрос в теме ниже для простого примера такого подхода:

Swift: дождитесь загрузки Firebase перед возвратом функции

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...