为什么我的@AppStorage不能在SwiftUI上工作



我正试图在我的项目中设置@AppStorage包装器。

我从JSON API中提取文本(请参阅DataModel(,并希望将结果存储在UserDefauts中。我希望数据被提取。出现并存储到@AppStorage中。当用户点击";获取下一个文本";,我想提取一首新的诗歌,并用最新的文本数据更新@AppStorage(这将删除过去存储的诗歌(。

目前,下面的代码正在构建,但在Text(currentPoemTitle)中没有显示任何内容。

数据模型

import Foundation
struct Poem: Codable, Hashable {
let title, author: String
let lines: [String]
let linecount: String
}
public class FetchPoem: ObservableObject {
// 1.
@Published var poems = [Poem]()

init() {
getPoem()
}

func getPoem() {
let url = URL(string: "https://poetrydb.org/random/1")!
// 2.
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
if let poemData = data {
// 3.
let decodedData = try JSONDecoder().decode([Poem].self, from: poemData)
DispatchQueue.main.async {
self.poems = decodedData
}
} else {
print("No data")
}
} catch {
print("Error")
}
}.resume()
}
}

测试视图

import SwiftUI
struct Test: View {

@ObservedObject var fetch = FetchPoem()
@AppStorage("currentPoemtTitle") var currentPoemTitle = ""
@AppStorage("currentPoemAuthor") var currentPoemAuthor = ""

var body: some View {

VStack{
Text(currentPoemTitle)

Button("Fetch next text") {
fetch.getPoem()
}

}.onAppear{
if let poem = fetch.poems.first {
currentPoemTitle = "(poem.title)"
currentPoemAuthor = "(poem.author)"
}
}

}

}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}

我错过了什么?谢谢

下面是一些代码编辑,让您开始工作。

  1. 我添加了AppStorageKeys来管理@AppStorage密钥,以避免在重新键入密钥字符串(即"currentPoemtTitle"(时出错

  2. 您的问题询问了如何使用数据更新@AppStorage,简单的解决方案是在FetchPo诗类中添加@AppStorage变量,并在下载数据后在Fetchpo诗类中设置它们。这也避免了对.onPeare函数的需要。

  3. 使用@ObservedObject的目的是使您的视图与数据保持同步。通过添加@AppStorage的额外层,您使@ObservedObject有点毫无意义。在视图中,我添加了一个Text((来直接使用@ObservedObject值显示标题,而不是依赖于@AppStorage。我不确定你是否想要这样,但这将完全消除对@AppStorage变量的需求。

  4. 我还使用Combine添加了一个getPoems2((函数,这是苹果公司下载异步数据的新框架。它使代码更容易/更高效。。。getPoems((和getPoems2((都工作并做相同的事情:(

代码:

import Foundation
import SwiftUI
import Combine
struct AppStorageKeys {
static let poemTitle = "current_poem_title"
static let poemAuthor = "current_poem_author"
}
struct Poem: Codable, Hashable {
let title, author: String
let lines: [String]
let linecount: String
}
public class FetchPoem: ObservableObject {
@Published var poems = [Poem]()
@AppStorage(AppStorageKeys.poemTitle) var poemTitle = ""
@AppStorage(AppStorageKeys.poemAuthor) var poemAuthor = ""

init() {
getPoem2()
}

func getPoem() {
let url = URL(string: "https://poetrydb.org/random/1")!
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
guard let poemData = data  else {
print("No data")
return
}

let decodedData = try JSONDecoder().decode([Poem].self, from: poemData)
DispatchQueue.main.async {
self.poems = decodedData
self.updateFirstPoem()
}
} catch {
print("Error")
}
}
.resume()
}

func getPoem2() {
let url = URL(string: "https://poetrydb.org/random/1")!

URLSession.shared.dataTaskPublisher(for: url)
// fetch on background thread
.subscribe(on: DispatchQueue.global(qos: .background))
// recieve response on main thread
.receive(on: DispatchQueue.main)
// ensure there is data
.tryMap { (data, response) in
guard
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
// decode JSON data to [Poem]
.decode(type: [Poem].self, decoder: JSONDecoder())
// Handle results
.sink { (result) in
// will return success or failure
print("poetry fetch completion: (result)")
} receiveValue: { (value) in
// if success, will return [Poem]
// here you can update your view
self.poems = value
self.updateFirstPoem()
}
// After recieving response, the URLSession is no longer needed & we can cancel the publisher
.cancel()
}


func updateFirstPoem() {
if let firstPoem = self.poems.first {
self.poemTitle = firstPoem.title
self.poemAuthor = firstPoem.author
}
}
}
struct Test: View {

@ObservedObject var fetch = FetchPoem()
@AppStorage(AppStorageKeys.poemTitle) var currentPoemTitle = ""
@AppStorage(AppStorageKeys.poemAuthor) var currentPoemAuthor = ""

var body: some View {

VStack(spacing: 10){

Text("App Storage:")
Text(currentPoemTitle)
Text(currentPoemAuthor)

Divider()

Text("Observed Object:")
Text(fetch.poems.first?.title ?? "")
Text(fetch.poems.first?.author ?? "")
Button("Fetch next text") {
fetch.getPoem()
}
}
}

}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}

最新更新