英文:
Unable to update JSON data in cacheDirectory
问题
这是你的代码的翻译部分:
已经过去一周了,我仍然无法弄清楚我的代码中缺少了什么。我只想将JSON文件保存在我的本地缓存目录中,然后将其加载到离线用户中。我的代码能够保存并从本地目录中读取,但如果我从GitHub存储库中更新了JSON数据,它就无法正常工作。只有原始的JSON数据被加载,只有在我更改了一些代码并重新启动模拟器时才加载了更新的JSON文件。看起来没有覆盖现有JSON文件的代码。
简而言之,我想要将最新的JSON数据保存在本地目录中,并在我对GitHub存储库进行更改时每次都将其加载回来。例如,我通过交替注释掉两个略有不同的JSON链接来测试它,以查看更新的JSON。请帮助我找出我的代码中解决不了的问题。提前感谢您。以下是我的代码:
class FetchingJSONFile5 {
@Published var allSongs: [TuunoLabu] = []
@Published var isNewFile: Bool = true
let folderOne = "TuunoLabu_Folder1"
init() {
createFolderOne()
downloadJSON()
if let lyrics = fetchTuunoLabu(from: "TuunoLabu") {
allSongs = lyrics
}
}
private func createFolderOne() {
guard let directoryOne = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent(folderOne).relativePath else { return }
if !FileManager.default.fileExists(atPath: directoryOne) {
do {
try FileManager.default.createDirectory(atPath: directoryOne, withIntermediateDirectories: true)
print("成功创建文件夹One")
//print(directoryOne)
} catch let error {
print("创建文件夹One时出错", error)
}
}
}
private func downloadJSON() {
guard let urlRequest = URL(string:
// 这个JSON包含id从1到748
"https://raw.githubusercontent.com/siantung/TuunoLabu/fac87c9d6b63f5e52cadb8873fb0d77cd0fc91e1/JSON/TuunoLabu.json"
// 这个JSON包含id从1到800
//"https://raw.githubusercontent.com/siantung/Hymn-iOS/main/HymnSong(02-25-2023).json"
)
else {
print("错误:找不到URL")
return
}
guard let directory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
.first?.appendingPathComponent(folderOne)
.appendingPathComponent("TuunoLabu.json") else { return }
//print(directory)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { [weak self] data, _, error in
guard let data = data, error == nil else { return }
do {
try data.write(to: directory, options: .atomic)
print("成功保存已下载的JSON新文件。")
self?.isNewFile = true
} catch let error {
print("解码错误:",error)
self?.isNewFile = false
}
}
dataTask.resume()
}
private func fetchTuunoLabu(from name: String) -> [TuunoLabu]? {
guard let directoryOne = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
.first?.appendingPathComponent(folderOne)
.appendingPathComponent("TuunoLabu.json") else {
print("在检索文件时出错,无法检索目录")
return nil
}
if FileManager.default.fileExists(atPath: directoryOne.relativePath) {
do {
let data = try Data(contentsOf: directoryOne)
return try JSONDecoder().decode([TuunoLabu].self, from: data)
} catch {
print("JSON解码错误:\(error)")
}
}
return nil
}
}
请注意,我已经将HTML编码(例如"
)翻译为正常的引号("
)以使代码更易读。如果有任何其他疑问,请随时告诉我。
英文:
it's been passed a week and I still couldn't figure out what am I missing in my code. I simply want to save the JSON file in my local cacheDirectory and load it back to the offline users. My code is able to save and read back from the local directory, but it doesn't work if I updated the JSON data from the GitHub repository. Only the original JSON data was loaded and the updated JSON file only loaded when I changed some codes and restart the simulator. Looks like there is no overwrite code to overwrite the existing JSON file.
Simply saying, I want to save the most updated JSON data in a local directory and load it back every time I made changes on the GitHub repository. For example, I tested it out with two slightly different JSON links by commenting out the two links alternatively to see the updated JSON. Please help me with what I can't figure out with my code. Thanks in advance. My code is below:
class FetchingJSONFile5 {
@Published var allSongs: [TuunoLabu] = []
@Published var isNewFile: Bool = true
let folderOne = "TuunoLabu_Folder1"
init() {
createFolderOne()
downloadJSON()
if let lyrics = fetchTuunoLabu(from: "TuunoLabu") {
allSongs = lyrics
}
}
private func createFolderOne() {
guard let directoryOne = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent(folderOne).relativePath else { return }
if !FileManager.default.fileExists(atPath: directoryOne) {
do {
try FileManager.default.createDirectory(atPath: directoryOne, withIntermediateDirectories: true)
print("Successfully created Folder One")
//print(directoryOne)
} catch let error {
print("Error while creating Folder One", error)
}
}
}
private func downloadJSON() {
guard let urlRequest = URL(string:
// This JSON has id 1 to 748
"https://raw.githubusercontent.com/siantung/TuunoLabu/fac87c9d6b63f5e52cadb8873fb0d77cd0fc91e1/JSON/TuunoLabu.json"
// This JSON has id 1 to 800
//"https://raw.githubusercontent.com/siantung/Hymn-iOS/main/HymnSong(02-25-2023).json"
)
else {
print("ERROR: Could not find the URL")
return
}
guard let directory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
.first?.appendingPathComponent(folderOne)
.appendingPathComponent("TuunoLabu.json") else { return }
//print(directory)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { [weak self] data, _, error in
guard let data = data, error == nil else { return }
do {
try data.write(to: directory, options: .atomic)
print("Successfully saved downloaded JSON new file.")
self?.isNewFile = true
} catch let error {
print("Error decoding: ",error)
self?.isNewFile = false
}
}
dataTask.resume()
}
private func fetchTuunoLabu(from name: String) -> [TuunoLabu]? {
guard let directoryOne = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
.first?.appendingPathComponent(folderOne)
.appendingPathComponent("TuunoLabu.json") else {
print("Error to retrieve directory while fetching file")
return nil
}
if FileManager.default.fileExists(atPath: directoryOne.relativePath) {
do {
let data = try Data(contentsOf: directoryOne)
return try JSONDecoder().decode([TuunoLabu].self, from: data)
} catch {
print("Error JSON decoding: \(error)")
}
}
return nil
}
}
答案1
得分: 1
这是翻译后的代码部分:
这是一个经典的时间问题。数据以异步方式下载,`fetchTuunoLabu` 的结果在 URLSession 的完成处理程序被调用之前被检索到。
使用你的代码,将表达式 `if let lyrics = ...` 放入 `try data.write` 后的完成处理程序中,或直接使用数据。
但是我建议利用 Swift 中的 `async/await` 和错误处理。
首先,用以下方法替换 `createFolderOne`。它根据缓存目录中的给定文件名返回**文件**的URL,并在必要时动态创建目录。可能会向调用者抛出错误。
```swift
private func cachedURL(fileName: String = "TuunoLabu.json") throws -> URL {
let directoryOne = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(folderOne)
if !FileManager.default.fileExists(atPath: directoryOne.path) {
try FileManager.default.createDirectory(at: directoryOne, withIntermediateDirectories: true)
}
return directoryOne.appendingPathComponent(fileName)
}
downloadJSON
方法可以简化为以下几行:
private func downloadJSON() async throws {
guard let url = URL(string:
"https://raw.githubusercontent.com/siantung/TuunoLabu/fac87c9d6b63f5e52cadb8873fb0d77cd0fc91e1/JSON/TuunoLabu.json") else {
throw URLError(.badURL)
}
let fileURL = try cachedURL()
let (data, _) = try await URLSession.shared.data(from: url)
try data.write(to: fileURL, options: .atomic)
isNewFile = true
}
再次强调,任何错误都会被抛出。使用 async
的好处是,尽管数据是异步加载的,但该方法的行为就像同步方法一样,等待结果。
第三个方法也包含了一些行:
private func fetchTuunoLabu(from name: String) throws -> [TuunoLabu] {
let fileURL = try cachedURL(fileName: name)
let data = try Data(contentsOf: fileURL)
return try JSONDecoder().decode([TuunoLabu].self, from: data)
}
类的其余部分如下:
class FetchingJSONFile5 {
@Published var allSongs: [TuunoLabu] = []
@Published var isNewFile: Bool = true
let folderOne = "TuunoLabu_Folder1"
init() {
Task {
do {
try await downloadJSON()
allSongs = try fetchTuunoLabu(from: "TuunoLabu.json")
} catch {
isNewFile = false
print(error)
// 或向用户展示更重要的信息
}
}
}
}
希望这有助于您理解和使用这些代码部分。如果有其他翻译需求,请告诉我。
英文:
It's a classic timing issue. The data is downloaded asynchronously, the result of fetchTuunoLabu
is retrieved before the completion handler of the URLSession is being called.
With your code put the expression if let lyrics = ...
into the completion handler after try data.write
or use the data directly
However I recommend to take advantage of async/await
and also of the error handling in Swift.
First of all replace createFolderOne
with the following method. It returns the URL to the file by given filename in the caches directory and creates the directory on the fly if it doesn't exist. A possible error is thrown
to the caller
private func cachedURL(fileName: String = "TuunoLabu.json") throws -> URL {
let directoryOne = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(folderOne)
if !FileManager.default.fileExists(atPath: directoryOne.path) {
try FileManager.default.createDirectory(at: directoryOne, withIntermediateDirectories: true)
}
return directoryOne.appendingPathComponent(fileName)
}
The downloadJSON
method can be reduced to these few lines
private func downloadJSON() async throws {
guard let url = URL(string:
"https://raw.githubusercontent.com/siantung/TuunoLabu/fac87c9d6b63f5e52cadb8873fb0d77cd0fc91e1/JSON/TuunoLabu.json") else {
throw URLError(.badURL)
}
let fileURL = try cachedURL()
let (data, _) = try await URLSession.shared.data(from: url)
try data.write(to: fileURL, options: .atomic)
isNewFile = true
}
Again, any error is thrown. The benefit of async
is although the data is loaded asynchronously the method behaves like a synchronous method and await
s the result
The third method contains also a few lines
private func fetchTuunoLabu(from name: String) throws -> [TuunoLabu] {
let fileURL = try cachedURL(fileName: name)
let data = try Data(contentsOf: fileURL)
return try JSONDecoder().decode([TuunoLabu].self, from: data)
}
The rest of the class is
class FetchingJSONFile5 {
@Published var allSongs: [TuunoLabu] = []
@Published var isNewFile: Bool = true
let folderOne = "TuunoLabu_Folder1"
init() {
Task {
do {
try await downloadJSON()
allSongs = try fetchTuunoLabu(from: "TuunoLabu.json")
} catch {
isNewFile = false
print(error)
// or show something more significant to the user
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论