当我的应用程序尝试下载时出现错误 JSON

Getting an error when my app tries to download JSON

我终于开始更新我的应用程序使用的 BonusData.json 文件。现在我在尝试加载数据时遇到错误。完整的代码在下面,但我得到 "JSON Download Failed",它包含在 downloadJSON 函数中。

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

中遇到了错误
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

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

有许多工具可以验证 JSON,但是 https://jsonlint.com/. If you paste the output from http://tourofhonor.com/BonusData.json 有一个可用的工具,它会为您突出显示格式错误并为您提供一些有关如何修复这些错误的指导。

我将专注于我认为是您问题的核心而非技术修复。

I'm not sure how to troubleshoot that any further.

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

decode 抛出有关异常的详细信息,您可以在收到错误时执行此操作。