我的应用尝试下载JSON

时间:2018-07-07 02:19:41

标签: ios json swift decoder

我终于开始更新我的应用程序使用的BonusData.json文件。现在,当我尝试加载数据时出现错误。完整的代码如下,但是我得到了downloadJSON函数中包含的“ JSON下载失败”。

如果我没看错我的代码,那意味着我在

中遇到错误
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
completed(posts.bonuses)

部分,但是我不确定如何进一步解决该问题。应该发生的事情是该应用程序查看服务器,下载JSON,然后将其保存在本地以用于填充UITableView。如果没有数据连接,则不必关心,而只需使用本地保存的版本。因为该应用程序正在加载空白,所以我假设它也无法按预期运行。

这是完整的代码:

import UIKit
import os.log
import Foundation

class BonusListViewController: UITableViewController {
    var bonuses = [JsonFile.JsonBonuses]()
    var filteredBonuses = [JsonFile.JsonBonuses]()
    var detailViewController: BonusDetailViewController? = nil

    let defaults = UserDefaults.standard

    let searchController = UISearchController(searchResultsController: nil)

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: Search Support
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchBar.placeholder = "Enter two letter state to filter"
        navigationItem.searchController = searchController
        definesPresentationContext = true

        // MARK: Settings Data Struct
        struct Constants {
            struct RiderData {
                let riderNumToH = "riderNumToH"
                let pillionNumToH = "pillionNumToH"
            }
            struct RallyData {
                let emailDestinationToH = "emailDestinationToH"
            }
        }
        //MARK: Load the bonuses
        print("About to call loadBonuses")
        loadBonuses { [weak self] bonuses in
            self?.bonuses = bonuses ?? []
            DispatchQueue.main.async {
                self?.tableView.reloadData()
            }
            print("loadBonuses called")
        }
    }

    // MARK: - Table View Configuration
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if isFiltering() {
            print("Showing \(filteredBonuses.count) Filtered Results")
            return filteredBonuses.count
        }

        print("Found \(bonuses.count) rows in section.")
        return bonuses.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "BonusListViewCell"
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusListViewCell else {
            fatalError("The dequeued cell is not an instance of BonusListViewCell.")
        }
        // let bonus = bonuses[indexPath.row]
        let bonus: JsonFile.JsonBonuses
        if isFiltering() {
            bonus = filteredBonuses[indexPath.row]
        } else {
            bonus = bonuses[indexPath.row]
        }

        let urlString = "http://tourofhonor.com/appimages/"+(bonus.imageName)
        let url = URL(string: urlString)
        cell.primaryImage.downloadedFrom(url: url!)
        cell.nameLabel.text = bonus.name.capitalized
        cell.bonusCodeLabel.text = bonus.bonusCode.localizedUppercase
        cell.categoryLabel.text = bonus.category
        cell.valueLabel.text = "\(bonus.value)"
        cell.cityLabel.text = "\(bonus.city.capitalized),"
        cell.stateLabel.text = bonus.state.localizedUppercase

        return cell
    }

    // MARK: Functions
    // MARK: - Fetch JSON from ToH webserver

    func downloadJSON(completed: @escaping ([JsonFile.JsonBonuses]?) -> ()) {
        let url = URL(string: "http://tourofhonor.com/BonusData.json")!
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error == nil, let data = data {
                do {
                    let posts = try JSONDecoder().decode(JsonFile.self, from: data)
                    completed(posts.bonuses)
                    print("URLSession did not fail")
                } catch {
                    print("JSON Download Failed")
                }
            } else {
                print("downloadJSON completed")
                completed(nil)
            }
        }.resume()
    }

    func saveBonuses(_ bonuses: [JsonFile.JsonBonuses], to url: URL) {
        try? FileManager.default.removeItem(at: url)
        do {
            let data = try JSONEncoder().encode(bonuses)
            try data.write(to: url)
            print("saveBonuses successful")
        } catch {
            print("Error saving bonuses to file:", error)
        }
    }

    func loadBonusesFromFile(_ url: URL) -> [JsonFile.JsonBonuses]? {
        do {
            let data = try Data(contentsOf: url)
            let bonuses = try JSONDecoder().decode([JsonFile.JsonBonuses].self, from: data)
            print("loadBonusesFromFile successful")
            return bonuses
        } catch {
            print("Error loading bonuses from file:", error)
            return nil
        }
    }

    func loadBonuses(completion: @escaping ([JsonFile.JsonBonuses]?) -> Void) {
        let localBonusesURL = try! FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("BonusData.json")
        downloadJSON { bonuses in
            if let bonuses = bonuses {
                completion(bonuses)
                self.saveBonuses(bonuses, to: localBonusesURL)
            } else {
                print("versions did not match")
                completion(self.loadBonusesFromFile(localBonusesURL))
            }
        }
    }

    func searchBarIsEmpty() -> Bool {
        // Returns true if the text is empty or nil
        return searchController.searchBar.text?.isEmpty ?? true
    }

    func filterContentForSearchText(_ searchText: String, scope: String = "All") {
        filteredBonuses = bonuses.filter({( bonus: JsonFile.JsonBonuses) -> Bool in
            return bonus.state.localizedCaseInsensitiveContains(searchText)
        })
        tableView.reloadData()
    }

    func isFiltering() -> Bool {
        return searchController.isActive && !searchBarIsEmpty()
    }

    // MARK: - Navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? BonusDetailViewController {
            destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
        }
    }
}

extension BonusListViewController: UISearchResultsUpdating {
    // MARK: - UISearchResultsUpdating Delegate
    func updateSearchResults(for searchController: UISearchController) {
        filterContentForSearchText(searchController.searchBar.text!)
    }
}

JSON托管在这里:http://tourofhonor.com/BonusData.json

2 个答案:

答案 0 :(得分:2)

您尝试下载的JSON格式似乎不正确。它缺少对象之间的逗号,并且在列表的末尾有一个额外的逗号。

有许多用于验证JSON的工具,但https://jsonlint.com/是一种可访问的工具。如果您将http://tourofhonor.com/BonusData.json的输出粘贴在那里,它将为您突出显示格式错误,并为您提供一些有关如何解决它们的指导。

答案 1 :(得分:2)

我将专注于我认为是您的问题的核心,而不是技术问题。

  

我不确定如何进一步解决该问题。

do {
  // ...
  let posts = try JSONDecoder().decode(JsonFile.self, from: data)
  // ...
} catch let error {
  // Do something with this error.
}

decode引发有关异常的详细信息,当您收到错误时可以执行此异常。