我有一个子视图,它是一个列表,其中包含来自ObservedObject的已发布数组变量上的ForEach。子视图显示在主ContentView的TabView和NavigationView中。
但是,当使用onAppear函数从网络请求更新列表时,列表不会呈现更新的数组。
将网络呼叫置于另一个回调中,例如,在另一个视图元素上轻按手势会导致视图正确更新。
我尝试使用willSet并手动调用objectWillChange,但没有成功。
ServerListView.swift:
struct ServerListView: View {
@ObservedObject var viewModel: ViewModel
@State private var searchText = ""
var body: some View {
List {
TextField("Search", text: self.$searchText)
ForEach(self.viewModel.servers) { server in
Text(server.name)
}
}
.navigationBarTitle(Text("Your Servers"))
.onAppear(perform: fetchServers)
}
func fetchServers() {
self.viewModel.getServers()
}
}
ViewModel.swift:
class ViewModel: ObservableObject {
private let service: ApiService
@Published var servers: [Server] = []
init(service: ApiService) {
self.service = service
}
func getServers() {
self.service.getServers { [weak self] result in
switch result {
case .failure(let error):
print(error)
case .success(let serverListModel):
DispatchQueue.main.async {
self?.servers = serverListModel.servers
}
}
}
}
}
如果此视图未放在父视图中,则该功能将按预期工作。
编辑以获取更多信息: ApiService类仅处理URLSession dataTask,并且该模型符合Decodable以便从JSON解码。
父视图按以下示例所示创建ServerListView,其中service.authenticated是一个Bool,它根据客户端是否通过API进行了身份验证设置为true或false。我已经在没有任何其他代码的情况下进行了测试,仅通过在主体内部创建ServerListView即可。可以看到相同的结果。
struct ContentView: View {
var service: ApiService
@ViewBuilder
var body: some View {
if service.authenticated {
TabView {
NavigationView {
ServerListView(ServerListViewModel(service: service))
}
.tabItem {
Image(systemName: "desktopcomputer")
Text("Servers")
}
}
}
else {
LoginView(service: service)
}
}
}
进一步测试后,将API调用放置在ServerViewList.swift的struct初始值设定项中,表示代码按预期工作。但这确实意味着API调用被放置了两次。该结构似乎被构造了两次,但是onAppear仅被调用了一次。