I have a strange infinite loop when using onAppear and I cannot identify the root of the problem. It only happens when the view is the detail view of a navigation view, but it works fine when it's the root view. Another interesting thing is that if I wrap the detail view in a NavigationView (so, now we have a navigation view inside a navigation view), then the issue does not appear anymore. Is this a bug in SwiftUI? Is conceptually my design OK? I mean, using onAppear like viewDidLoad to trigger the initial sequence. Thanks for suggestions.
Here is the source code. ContentView.swift:
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ContentViewModel()
var body: some View {
NavigationView {
VStack {
Group {
switch viewModel.state {
case .loading:
Text("Loading...")
case .loaded:
HStack {
Text("Loaded")
Button("Retry") {
viewModel.fetchData()
}
}
}
}
.padding(.bottom, 20)
NavigationLink("Go to detail screen", destination: DetailView())
}
}
.onAppear() {
viewModel.fetchData()
}
}
}
class ContentViewModel: ObservableObject {
enum State {
case loading
case loaded
}
@Published var state: State = .loading
func fetchData() {
state = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.state = .loaded
}
}
}
And here the code of the detail view:
import SwiftUI
struct DetailView: View {
@StateObject var viewModel = DetailViewModel()
var body: some View {
Group {
switch viewModel.state {
case .loading:
Text("Loading...")
case .loaded:
HStack {
Text("Loaded")
Button("Retry") {
viewModel.fetchData()
}
}
}
}
.onAppear() {
print("infinite loop here")
viewModel.fetchData()
}
}
}
class DetailViewModel: ObservableObject {
enum State {
case loading
case loaded
}
@Published var state: State = .loading
func fetchData() {
state = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.state = .loaded
}
}
}
Here I attach the project: https://www.dropbox.com/s/5alokj3q81jbpj7/TestBug.zip?dl=0
I'm using Xcode Version 12.5.1 (12E507) and iOS 14.5
Thanks a lot.