Why is SwiftUI not setting a state variable from decoded JSON?(为什么SwiftUI没有从解码的JSON设置状态变量?)

I am trying to download a JSON list of first names and use them to randomly pick a name. However when I step through in the debugger I see that self.names is not being set to the parsed JSON. Why is this?

struct ContentView: View {

  @State var names:[String] = []
  init(){
      getNames()
  }
  var body: some View {
      List {displays person objects from coredata}
      Button(action: addItem)
  }

  func getNames(){
    // URL & JSON setup
    session.dataTask(with: request) { data, response, error in
        guard error == nil else {return}
        do {
            let decoder = JSONDecoder()
            let decodedNames = try decoder.decode([String].self, from: data!)
            DispatchQueue.main.async {
                self.names = decodedNames // self.names not being set here
            }
        }
        catch {}
    }.resume()
  }
  
  func addItem() {
      let p = Person(context: viewContext)
      p.age = String(Int.random(in: 1...100))
      p.name = self.names[Int.random(in: 0..<self.names.count)] // crashes here
      try! viewContext.save()
  }
  

Solution:

Views in SwiftUI do not have a guaranteed lifespan. They’re transitive by nature and the system can rebuild them at any time. If you have an asynchronous method in a View, there is no guarantee that this same instance of the View will exist in the hierarchy by the time it returns.

For this reason, it’s generally recommended that you move asynchronous code to a view model (ObservableObject with a @Published property). The ObservableObject will have a guaranteed lifespan and can communicate the data back to the view via the @Published property.

————————

我试图下载一个名的JSON列表,并使用它们随机选择一个名字。但是,当我在调试器中单步执行时,我看到self.names没有被设置为已解析的JSON。为什么会这样?

struct ContentView: View {

  @State var names:[String] = []
  init(){
      getNames()
  }
  var body: some View {
      List {displays person objects from coredata}
      Button(action: addItem)
  }

  func getNames(){
    // URL & JSON setup
    session.dataTask(with: request) { data, response, error in
        guard error == nil else {return}
        do {
            let decoder = JSONDecoder()
            let decodedNames = try decoder.decode([String].self, from: data!)
            DispatchQueue.main.async {
                self.names = decodedNames // self.names not being set here
            }
        }
        catch {}
    }.resume()
  }
  
  func addItem() {
      let p = Person(context: viewContext)
      p.age = String(Int.random(in: 1...100))
      p.name = self.names[Int.random(in: 0..<self.names.count)] // crashes here
      try! viewContext.save()
  }
  

解决方法:

SwiftUI中的视图没有保证的使用寿命。它们本质上是可传递的,系统可以随时重建它们。如果在视图中有一个异步方法,则不能保证该视图的同一实例在返回时会存在于层次结构中。

因此,通常建议您将异步代码移动到视图模型(带有@Published属性的observeObject)。ObserveObject将有一个有保证的生命周期,并且可以通过@Published属性将数据传回视图。