调用 ObservableObject class 初始化程序以更新 SwiftUI 中的列表

Calling ObservableObject class initialiser to update List in SwiftUI

我有一个 List 更新了一个 Fetch class,一个 ObservableObject。它有一个初始化函数。这就是 Fetch class.


@Published private(set) var items: [ItemsResult] = []
    
    init() {
        let url = URL(string: "[redacted]")!
        
        URLSession.shared.dataTask(with: url) {(data, response, error) in
            do {
                if let itemsData = data {
                    let decodedData = try JSONDecoder().decode([ItemsResult].self, from: itemsData)
                    DispatchQueue.global(qos: .utility).async {
                        DispatchQueue.main.async {
                            print("running task")
                            self.items = decodedData
                        }
                    }
                    
                    print(self.items)
                } else {
                    print("No data")
                }
            } catch {
                print("Error: \(error)")
            }
        }.resume()
    }

构建应用程序时,它会正确显示 API 返回的数据并且与数据库匹配。但是,当我点击/单击一个以将其删除,或使用我添加的文本区域添加一个新项目时,它不会更新。

struct TickrApp: View {
    @EnvironmentObject var fetch: Fetch

    var body: some View {
        NavigationView {
            Form {
                Section {
                    VStack(alignment: .center) {
                        Text("Welcome to Tickr")
                    }
                }
                Section {
                    List(fetch.items) { item in
                        CheckView(checked: item.done, title: item.content.replacingOccurrences(of:"_", with: " "))
                    }
                }
                AddItemView()
            }.navigationBarTitle(Text("Tickr"))
        }
    }
}

数据库正在更新,如图所示,当我记录他们响应的解码数据时,但是在每个中我只调用 Fetch()。三种情况下的请求都是一样的。

其中一个调用,用于文本输入。

  func toggle() {
    checked = !checked
        let url = URL(string: "")!
        var req = URLRequest(url: url)
        req.httpMethod = "POST"
        
        let task = URLSession.shared.dataTask(with: req) { data, response, error in
            guard let _ = data,
                let response = response as? HTTPURLResponse,
                error == nil else {
                print("error", error ?? "Unknown error")
                return
            }
    
            guard (200 ... 299) ~= response.statusCode else {
                print("statusCode should be 2xx, but is \(response.statusCode)")
                print("response = \(response)")
                return
            }
        }
        task.resume()
        Fetch()

为了直观地更新列表,我需要完全退出该应用程序/重新运行它,以便正确显示新的 and/or 已删除项目。没有显示有关后台发布更改或任何内容的错误。

您似乎正在尝试调用 Fetch() 来刷新您的数据。有两件事会成为问题:

  1. 您在 dataTask 完成处理程序的 外部 调用它。这意味着它可能会在写入完成之前被调用

  2. 调用 Fetch() 只会创建 Fetch 的新实例,而您真正想做的是更新现有实例的结果。

假设您截取的第一个代码来自Fetch。我会把它改成这样:

class Fetch: ObservableObject {
  @Published private(set) var items: [ItemsResult] = []

  init() {
    performFetch()
  }

  func performFetch() {
    //your existing fetch code that was in `init`
  }
}

然后,在您的 AddItemViewCheckView 中,确保您有此行:

@EnvironmentObject var fetch: Fetch

这将确保您使用的是 Fetch 相同实例 ,因此您的列表将反映相同的结果集合。

完成 toggle() 等操作后,调用 self.fetch.performFetch() 更新结果。所以,你的最后一个代码片段会变成这样:

let task = URLSession.shared.dataTask(with: req) { data, response, error in 
  //guard statements to check for errors
  self.fetch.performFetch() //perform your refresh on the *existing* `Fetch` instance
}

更大的重构将涉及将异步代码(如 toggle)移动到视图模型,而不是在 View 中执行 any代码。此外,由于您使用的是 SwiftUI,请考虑使用 URLSession 发布者使用 Combine:https://developer.apple.com/documentation/foundation/urlsession/processing_url_session_data_task_results_with_combine