【发布时间】:2018-07-03 01:59:52
【问题描述】:
我仍然被困住了,为此我已经把头撞到墙上好几个星期了。所以在这里我再问一次,这次以更完整的方式。我只想完成这项工作。我一直在尝试尽可能多地使用 Swift 4(因为我正在学习坚持一套规则/语法似乎更容易,但在这一点上,我不在乎使用什么语言,只要它有效这样我就可以继续我需要对应用执行的其他操作了。
目标:查看 JSON 的本地版本,并将其与托管版本进行比较。如果托管较新,请将本地版本替换为较新的版本。然后解析本地JSON文件创建UITableView,并按状态分节。
问题:它使用了一种从网站实时解析它的旧方法,但显示重复和错误的部分计数。现在似乎将本地与托管进行了正确比较,但 UITableView 现在根本没有被填充。我怀疑我所有的问题都在 tableView 部分,但我已经尝试了 10 万亿种不同的方法,但都没有奏效。我假设我没有正确地将它指向本地 JSON 文件。
代码: 这是我的整个 ViewController:
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,它提供了 JSON 解析的结构:
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]
}
我需要有人像我 5 岁一样解释它。我觉得我理解我的函数在做什么,但我终其一生都无法弄清楚为什么它不起作用,也不知道为什么它什么时候起作用工作(使用旧方法),这些部分完全不合时宜。如果您看到我提出的问题,我很抱歉,我只是想学习如何做到这一点,这样我就可以自给自足,但这一篇对我来说没有意义。
【问题讨论】:
-
如果您必须从服务器下载整个 json 文件以检查版本,那么进行版本检查有什么意义?始终使用您下载的版本。这将大大简化您目前无处不在的逻辑。
-
没有显示的原因是因为您为
bonuses属性设置值的唯一位置是您从未调用过的downloadJSON。 -
我不认为它是在下载托管的 JSON 来比较版本。我同意如果它无论如何都要下载它,那么在打开时尝试下载就可以了。这里的意图是应用程序可以在没有数据连接的情况下运行,因此如果检查失败,仍然有一个本地文件可以依赖。
-
至于
bonuses属性,它只能从downloadJSON调用,因为这是我使用的旧方法。我尝试将其更改为其他各种东西以使用更新的方法,但都失败了。我把那个代码留在了那里,所以我可以恢复使用downloadJSON进行比较。一旦新方法正常工作,它将被删除。 -
缓存 json 以供离线使用是有效的,但您当前的实现只会在没有连接时崩溃。你的
downloadJSON方法比新的尝试更接近你想要的。
标签: ios swift xcode uitableview