SwiftUI doesn't seem to persist @StateObjects for list rows, when the row is embedded inside a container like a stack or NavigationLink. Here's an example:
class MyObject: ObservableObject {
init() { print("INIT") }
}
struct ListView: View {
var body: some View {
List(0..<40) { _ in
NavigationLink(destination: Text("Dest")) {
ListRow()
}
}
}
}
struct ListRow: View {
@StateObject var obj = MyObject()
var body: some View {
Text("Row")
}
}
As you scroll down the list, you see "INIT" logged for each new row that appears. But scroll back up, and you see "INIT" logged again for every row - even though they've already appeared.
Now remove the NavigationLink:
List(0..<40) { _ in
ListRow()
}
and the @StateObject behaves as expected: exactly one "INIT" for every row, with no repeats. The ObservableObject is persisted across view refreshes.
What rules does SwiftUI follow when persisting @StateObjects? In this example MyObject might be storing important state information or downloading remote assets - so how do we ensure it only happens once for each row (when combined with NavigationLink, etc)?
ListRowprobably shouldn't store any@StateObjects. Instead, they should be persisted outside of the row, then passed in when theListRowis initialized. - NRitH