Загрузить асинхронный локальный файл JSON - PullRequest
0 голосов
/ 08 мая 2018

Я новичок в Swift и способ синхронизации / асинхронной загрузки файла. У меня есть большой JSON-файл на локальном компьютере для приложения iPad о футболе со списком и статистикой футболистов.

На данный момент я загружаю весь список игроков внутри массива словарей и я позволяю пользователю искать конкретного игрока

func loadJSON() {
    /// Load Json File
    if let path = Bundle.main.path(forResource: "players", ofType: "json") {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
            let jsonObj = try JSON(data: data)

            /// For Player in JSON Serialize values
            for (_,subJson):(String, JSON) in jsonObj["PackData"]["PlayerData"]["P"] {

                let firstName = subJson["_f"].stringValue
                let lastName = subJson["_s"].stringValue
                let id = subJson["_id"].stringValue
                let dateOfBirth = subJson["_d"].stringValue
                let height = subJson["_h"].stringValue
                let weight = subJson["_w"].stringValue
                let image = subJson["_i"].stringValue


                let player = Player(id: id, firstName: firstName, lastName: lastName, dateOfBirth: dateOfBirth, height: height, weight: weight, image: image)

                /// Append Player in players Array
                players.append(player)

            }

Поскольку я использую loadJSON() в ViewDidLoad, приложение останавливается на несколько секунд и использует много памяти при переходе к этому представлению.

Как правильно обрабатывать / реализовывать что-то вроде поиска в БД в асинхронном режиме?

EDIT: Я уже пытался использовать диспетчер DispatchQueue.global(qos: .background).async, но я получаю сообщение об ошибке: indexPath.row out of range на player = filteredPlayers[indexPath.row]

 // create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // create a new cell if needed or reuse an old one

    let cell:UITableViewCell = self.searchTableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
    let player: Player

    /// Return a different table if is searching or not
    if isFiltering() {
        player = filteredPlayers[indexPath.row]
    } else {
        player = players[indexPath.row]
    }
    cell.textLabel?.text = player.firstName! + " " + player.lastName!

    cell.textLabel?.textColor = UIColor.white

    return cell

}

Ответы [ 5 ]

0 голосов
/ 08 мая 2018

Вам нужно отправить длинную задачу в фоновую очередь и отправить результат обратно в основную.

Мой упрощенный пример json:

{
    "person": "Bob"
}

Создать метод загрузки JSON

func loadJSON(completion: @escaping (_ data: String?, _ error: Error?) -> ())  {
    var person: String?
    var receivedError: Error?

    /// Load json file and parse in background queue
    DispatchQueue.global(qos: .background).async {
        let path = Bundle.main.path(forResource: "myJSON", ofType: "json")!
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
            let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
            let jsonDictionary =  json as! Dictionary<String, Any>

            person = jsonDictionary["person"] as? String
        } catch {
            receivedError = error
        }

        // Dispatch the found value to main queue
        DispatchQueue.main.async {
            completion(person, receivedError)
        }
    }
}

И назовите это в своем коде:

loadJSON { (data, error) in
    if let retrievedData = data {
        print(retrievedData)
        // It is safe to assign the value to UI objects 
        // because the callback is on the main thread
    }
}
0 голосов
/ 08 мая 2018

Вам нужно использовать в DispatchQueue в фоновом режиме,

func loadJSON() {
    /// Load Json File
    DispatchQueue.global(qos: .background).async{
        if let path = Bundle.main.path(forResource: "players", ofType: "json") {
            do {
                let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
                let jsonObj = try JSON(data: data)

                /// For Player in JSON Serialize values
                for (_,subJson):(String, JSON) in jsonObj["PackData"]["PlayerData"]["P"] {

                    let firstName = subJson["_f"].stringValue
                    let lastName = subJson["_s"].stringValue
                    let id = subJson["_id"].stringValue
                    let dateOfBirth = subJson["_d"].stringValue
                    let height = subJson["_h"].stringValue
                    let weight = subJson["_w"].stringValue
                    let image = subJson["_i"].stringValue


                    let player = Player(id: id, firstName: firstName, lastName: lastName, dateOfBirth: dateOfBirth, height: height, weight: weight, image: image)

                    /// Append Player in players Array
                    players.append(player)

                }
            }
        }
    }
}
0 голосов
/ 08 мая 2018

Выполните loadJSON() в фоновом потоке, а затем верните результат обратно в главное с помощью обратного вызова или присвоения свойства. Код:

DispatchQueue(label: "jsonLoading", qos: .background).async {
    let players = self.loadJSON()
    DispatchQueue.main.async {
        // Handle data in the main thread in whatever way you need, eg:
        self.players = players
    }
}
0 голосов
/ 08 мая 2018

Перейти к фоновому потоку, чтобы выполнить долгую работу

0 голосов
/ 08 мая 2018
func loadJSON(completion: @escaping ()->()) {
    /// Above Parameter is a completion handler which informs user that some task have been done.
    //Do such heavy operations like json loading from local file in a back ground thread so your main thread doesn't get affected.
    DispatchQueue.global(qos: .background).async {
        if let path = Bundle.main.path(forResource: "players", ofType: "json") {
            do {
                let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
                let jsonObj = try JSON(data: data)

                /// For Player in JSON Serialize values
                for (_,subJson):(String, JSON) in jsonObj["PackData"]["PlayerData"]["P"] {

                    let firstName = subJson["_f"].stringValue
                    let lastName = subJson["_s"].stringValue
                    let id = subJson["_id"].stringValue
                    let dateOfBirth = subJson["_d"].stringValue
                    let height = subJson["_h"].stringValue
                    let weight = subJson["_w"].stringValue
                    let image = subJson["_i"].stringValue


                    let player = Player(id: id, firstName: firstName, lastName: lastName, dateOfBirth: dateOfBirth, height: height, weight: weight, image: image)

                    /// Append Player in players Array
                    players.append(player)
                }
                completion()
            }
            catch _ {

            }
        }
    }
}

Теперь вызовите вышеуказанный метод в вашем классе viewController или любом другом, который вам нужен.

func yourViewControllerMethod() {
    loadJSON {
        // This Block will execute when your json is loaded and parsed completely.
        DispatchQueue.main.async{
             // This is the main thread now you are again here on your main thread after your journey from background thread.

        }
    }
}

Никогда не делайте ничего с вашими UI элементами в background thread

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