读取本地JSON文件并使用它来填充UITableView [英] Reading local JSON file and using it to populate UITableView
问题描述
我正在编写一个需要查看本地JSON文件的应用程序,然后将其版本与我在网站上托管的版本进行比较.如果不匹配,请从网上下载一个并将其保存在本地.如果它们确实匹配,则继续并使用本地JSON文件.此版本信息位于JSON文件本身中.
I am writing an app that needs to look at a local JSON file, then compare it's version to one I have hosted on a website. If they don't match, download the one from the web and save it locally. If they do match, then continue on and use the local JSON file. This version info is in the JSON file itself.
以前,我的应用只是解析在线数据并直接使用.然后,它将使用JSON数据填充UITableView.现在,我正在使用本地文件,现在不会填充UITableView,并且不确定如何修复它.通过阅读新功能,我认为我的问题是我没有使用JSONDecoder(),而是使用JSONSerialization(),因此无法将其指向所需的特定元数据.
Previously, my app would simply parse the online data and use that directly. It would then populate the UITableView using the JSON data. Now that I am using my local file, the UITableView isn't getting populating, and I'm not certain how to fix it. From reading the new function, I think my issue is that I'm not using JSONDecoder(), and instead using JSONSerialization(), and therefore I can't point it at the specific metadata I want.
18年6月26日编辑(以下是我的BonusListViewController.swift文件):
26 Jun 18 Edit (Below is my BonusListViewController.swift file):
//
// BonusListViewController.swift
// Tour of Honor
//
// Created by Tommy Craft on 6/6/18.
// Copyright © 2018 Tommy Craft. All rights reserved.
//
import UIKit
import os.log
import Foundation
class BonusListViewController: UITableViewController {
var bonuses = [JsonFile.JsonBonuses]()
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
// MARK: Data Structures
// Settings Struct
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
//MARK: Check for updated JSON file
checkJSON()
//MARK: Trigger JSON Download
/*
downloadJSON {
print("downloadJSON Method Called")
}
*/
}
// MARK: - Table View Configuration
// MARK: Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
print("Found \(bonuses.count) sections.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Found \(bonuses.count) rows in section.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = bonuses[indexPath.section].name.capitalized
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetail", sender: self)
}
// MARK: - Table View Header
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return bonuses[section].state
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 3
}
// MARK: Functions
// MARK: - Download JSON from ToH webserver
func downloadJSON(completed: @escaping () -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")
URLSession.shared.dataTask(with: url!) { [weak self] (data, response, error) in
if error == nil {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data!)
DispatchQueue.main.async {
completed()
}
print("Downloading Updated JSON (Version \(posts.meta.version))")
print(posts.bonuses.map {$0.bonusCode})
print(posts.bonuses.map {$0.state})
self?.bonuses = posts.bonuses
self?.defaults.set("downloadJSON", forKey: "jsonVersion") //Set version of JSON for comparison later
DispatchQueue.main.async {
//reload table in the main queue
self?.tableView.reloadData()
}
} catch {
print("JSON Download Failed")
}
}
}.resume()
}
func checkJSON() {
//MARK: Check for updated JSON file
let defaults = UserDefaults.standard
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
var hostedJSONVersion = ""
let jsonData = try! Data(contentsOf: jsonURL!)
let jsonFile = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String : Any]
let metaData = jsonFile["meta"] as! [String : Any]
hostedJSONVersion = metaData["version"] as! String
let localJSONVersion = defaults.string(forKey: "jsonVersion")
if localJSONVersion != hostedJSONVersion {
print("L:\(localJSONVersion!) / H:\(hostedJSONVersion)")
print("Version Mismatch: Retrieving lastest JSON from server.")
updateJSONFile()
} else {
//Retrieve the existing JSON from documents directory
print("L:\(localJSONVersion!) / H:\(hostedJSONVersion)")
print("Version Match: Using local file.")
let fileURL = defaults.url(forKey: "pathForJSON")
do {
let localJSONFileData = try Data(contentsOf: fileURL!, options: [])
let myJson = try JSONSerialization.jsonObject(with: localJSONFileData, options: .mutableContainers) as! [String : Any]
//Use my downloaded JSON file to do stuff
print(myJson)
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
func updateJSONFile() {
print("updateJSONFile Method Called")
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
let itemName = "BonusData.json"
let defaults = UserDefaults.standard
do {
let directory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = directory.appendingPathComponent(itemName)
let jsonData = try Data(contentsOf: jsonURL!)
let jsonFile = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String : Any]
let metaData = jsonFile!["meta"] as! [String : Any]
let jsonVersion = metaData["version"]
print("JSON VERSION ", jsonVersion!)
try jsonData.write(to: fileURL, options: .atomic)
defaults.set(fileURL, forKey: "pathForJSON") //Save the location of your JSON file to UserDefaults
defaults.set(jsonVersion, forKey: "jsonVersion") //Save the version of your JSON file to UserDefaults
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
}
这是JsonFile.swift:
And here is the JsonFile.swift:
import Foundation
struct JsonFile: Codable {
struct Meta: Codable {
let fileName: String
let version: String
}
struct JsonBonuses: Codable {
let bonusCode: String
let category: String
let name: String
let value: Int
let city: String
let state: String
let flavor: String
let imageName: String
}
let meta: Meta
let bonuses: [JsonBonuses]
}
这与在更新的版本中不使用JSONDecoder()有关,还是我走错了路?另外,如何获取这些新数据以与UITableView一起使用?
Is this related to not using JSONDecoder() in my updated version or am I going down the wrong path there? Also, how do I get this new data to work with the UITableView?
推荐答案
首先,您错误地解析了JSON值.您需要首先了解JSON格式.您转到JSON文件链接,然后进行分析.如果以"{"开头,则为字典,如果以"["开头,则为数组.在您的情况下,它是一个字典,然后是字符串(元",奖金").因此,我们知道我们的键是字符串.接下来,我们看一下我们的价值观.对于元",我们有一个字符串字典:String;对于奖金",我们有一组词典.
因此,我们的JSON格式为[String:Any],也可以写为Dictionary<String, Any>
.
First of all, you are parsing JSON values incorrectly. You need to first understand your JSON format. You go to your JSON file link, and analyze it. If it starts with a "{", then it is a Dictionary, if it starts with a "[", then it is an Array. In your case, it is a Dictionary, then there come the keys which are Strings ("meta", "bonuses"). So, we know our keys are Strings. Next, we look at our values. For "meta" we have a Dictionary of String : String; for "bonuses" we have an Array of Dictionaries.
So, our JSON format is [String : Any], or it can be written Dictionary<String, Any>
.
下一步,正在访问字典中的那些值.
Next step, is accessing those values in the Dictionary.
func updateJSONFile() {
print("updateJSONFile Method Called")
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
let itemName = "BonusData.json"
let defaults = UserDefaults.standard
do {
let directory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = directory.appendingPathComponent(itemName)
let jsonData = try Data(contentsOf: jsonURL!)
let jsonFile = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String : Any]
let metaData = jsonFile!["meta"] as! [String : Any]
let jsonVersion = metaData["version"]
print("JSON VERSION ", jsonVersion!)
try jsonData.write(to: fileURL, options: .atomic)
defaults.set(fileURL, forKey: "pathForJSON") //Save the location of your JSON file to UserDefaults
defaults.set(jsonVersion, forKey: "jsonVersion") //Save the version of your JSON file to UserDefaults
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
然后,再次访问本地保存的文件时,您必须解析JSON以检查版本:
Then, when you access your locally saved file, again, you have to parse the JSON to check the versions:
func checkJSON() {
//MARK: Check for updated JSON file
let defaults = UserDefaults.standard
let hostedJSONFile = "http://tourofhonor.com/BonusData.json"
let jsonURL = URL(string: hostedJSONFile)
var hostedJSONVersion = ""
let jsonData = try! Data(contentsOf: jsonURL!)
let jsonFile = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String : Any]
let metaData = jsonFile["meta"] as! [String : Any]
hostedJSONVersion = metaData["version"] as! String
let localJSONVersion = defaults.string(forKey: "jsonVersion")
if localJSONVersion != hostedJSONVersion {
print("\(localJSONVersion) : \(hostedJSONVersion)")
updateJSONFile()
} else {
//Retrieve the existing JSON from documents directory
print("\(localJSONVersion) : \(hostedJSONVersion)")
print("Local JSON is still the latest version")
let fileUrl = defaults.url(forKey: "pathForJSON")
do {
let localJSONFileData = try Data(contentsOf: fileUrl!, options: [])
let myJson = try JSONSerialization.jsonObject(with: localJSONFileData, options: .mutableContainers) as! [String : Any]
//Use my downloaded JSON file to do stuff
DispatchQueue.main.async {
//reload table in the main queue
self.tableView.reloadData()
}
} catch {
print(error)
}
}
}
别忘了在Info.plist文件中允许任意加载,因为JSON文件托管在没有https的网站上.
Don't forget to allow arbitrary loads in your Info.plist file, because your JSON file is hosted on a website without https.
这篇关于读取本地JSON文件并使用它来填充UITableView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!