获取 JSON,附加到数组:转义闭包捕获变异的“自我”参数
Posted
技术标签:
【中文标题】获取 JSON,附加到数组:转义闭包捕获变异的“自我”参数【英文标题】:Fetching JSON, appending to array: Escaping closure captures mutating 'self' parameter 【发布时间】:2021-04-30 17:50:46 【问题描述】:我已经准备好a simple test project at Github 来演示我的问题:
我有一个 SwiftUI 列表,我尝试在其中显示 var items:[String]
。
当我只有一个像下面这样的硬编码数组时 - 它可以正常工作并在 iPhone 中显示:
items = (1...200).map number in "Item \(number)"
但是当我尝试获取 JSON 网页并将结果附加到 items
时,我得到了错误:
转义闭包捕获变异的“自我”参数
我知道items.append(str)
行从 dataTask 闭包中修改了父 ContentView 对象,这出于某种原因并不好......但是如何修复我的代码呢?
import SwiftUI
struct TopResponse: Codable
let data: [Top]
struct Top: Codable
let uid: Int
let elo: Int
let given: String
let photo: String?
let motto: String?
let avg_score: Double?
let avg_time: String?
struct ContentView: View
var items:[String];
init()
items = (1...200).map number in "Item \(number)"
let url = URL(string: "https://slova.de/ws/top")!
let task = URLSession.shared.dataTask(with: url)
data, response, error in
let decoder = JSONDecoder()
guard let data = data else return
do
let tops = try decoder.decode(TopResponse.self, from: data)
for (index, top) in tops.data.enumerated()
let str = "\(index + 1): \(top.given)"
items.append(str) // this results in compile error!
catch
print("Error while parsing: \(error)")
task.resume()
var body: some View
List(items, id: \.self) item in
Text(item)
我应该将items
移出视图吗?
我的最终目标是在 Core Data 中包含 JSON 数据,然后从中更新/通知列表。
我在 android 中有一个这样的应用程序(结构为 MVVM),现在我正在尝试将它移植到 SwiftUI,作为一个 Swift 新手。
更新:
我已按照 achu 的建议添加了 a view model file(谢谢!),它确实有效,但只有当我拖动列表时,列表才会更新为新项目。并且有一个警告
[SwiftUI] 不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过接收(on:) 等运算符)。
【问题讨论】:
修改数组时实际上是在修改结构体,但结构体是一个值对象,因此您可以这样做。您应该有一个管理数据的类,并且在您的视图结构中没有该代码。 这个 SO question + answer 将有助于理解问题。 不要滥用 SwiftUI 视图作为 控制器。在额外的ObservableObject
类和publish
中加载数据
这是我在 SwiftUI 中如何做到这一点的教程:youtube.com/watch?v=h42OHc5CRBQ .. 虽然我建议学习使用组合方法:youtube.com/watch?v=fdxFp5vU6MQ&t=0s。这是 MVVM 上的另一个内容,可帮助您找到更好的方向 youtube.com/watch?v=wEf1YS4vyW8
【参考方案1】:
我会将items
移至 ViewModel 并最终将服务调用移至 APIManager 类
编辑:UI 更新应该在主线程中。添加了对ViewModel
init()
的服务调用。
struct TestView: View
@ObservedObject var viewModel = TestViewModel()
var body: some View
List(viewModel.items, id: \.self) item in
Text(item)
class TestViewModel: ObservableObject
@Published var items: [String] = []
init()
self.fetchData()
func fetchData()
let url = URL(string: "https://slova.de/ws/top")!
let task = URLSession.shared.dataTask(with: url)
data, response, error in
let decoder = JSONDecoder()
guard let data = data else return
do
let tops = try decoder.decode(TopResponse.self, from: data)
for (index, top) in tops.data.enumerated()
let str = "\(index + 1): \(top.given)"
self.updateItems(str)
catch
print("Error while parsing: \(error)")
task.resume()
func updateItems(_ str: String)
DispatchQueue.main.async
self.items.append(str)
【讨论】:
谢谢,它几乎可以工作 - 请查看我更新的问题 更新了代码。您必须在 init 上调用 webservice 方法,并且 UI 更改应该在主线程中 我删除了items = (1...200).map number in "Item \(number)"
否则它将附加到这些项目中。如果你想在你的 items 数组中添加它,你可以添加它以上是关于获取 JSON,附加到数组:转义闭包捕获变异的“自我”参数的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 3.0 中的转义闭包中改变自我(结构/枚举)