List rows disappearing when scrolling

I have a List containing ItemRow views based on an ItemDetails object. The content is provided by a model which pulls it from Core Data.

When I scroll through the list one or two of the rows will disappear and reappear when I scroll back up. I have a feeling it's because the state is being lost?

Here's some relevant info (only necessary parts of the files are provided):

-- ModelData.swift:
@Observable
class ModelData {
	var allItems: [ItemDetails] = coreData.getAllItems()
	...
}


-- ItemDetails.swift:
struct ItemDetails: Identifiable, Hashable, Equatable {
	public let id: UUID = UUID()
	public var itemId: String  // Also unique, but used for a different reason
	...
}


-- MainApp.swift:
let modelData: ModelData = ModelData()  // Created as a global

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
	// Methods in here (and in lots of other places) use `modelData`, which is why it's a global
}

@main
struct MainApp: App {
	var body: some Scene {
		WindowGroup {
			MainView()
		}
	}
	...
}


-- MainView.swift:

struct MainView: View {
	var body: some View {
		List {
			ForEach(modelData.allItems, id: \.id) { item in
				ItemRow(item)
			}
		}
	}
}


struct ItemRow: View, Equatable {
	var item: ItemDetails
	var body: some View {
		...
	}
	static func == (lhs: Self, rhs: Self) -> Bool {
		lhs.item == rhs.item
	}
}

There's obviously more code in the app than that, but it's not relevant to the issue.

I've tried:

  • ItemRow(item).equatable()
  • Wrapping ItemRow in an EquatableView
  • Giving the List a unique id
  • Using class ModelData: ObservableObject and @StateObject for modelData

None made any difference.

I'm using iOS/iPadOS 26.0.1, and I see it on my physical iPhone 17 Pro Max and iPad Pro 11-inch M4, but I don't see it in the equivalent simulators on those versions. The Simulator also doesn't exhibit this for versions 17.5 and 18.5, and I have no physical devices on 17.5/18.5 to check.

Should I be doing as I currently am, where I create modelData as a global let so I can access it everywhere, or should I pass it through the view hierarchy as an Environment variable, like @Environment(ModelData.self) var modelData: ModelData? Bear in mind that some functions are outside of the view hierarchy and cannot access modelData if I do this. Various things like controllers that need access to values in modelData cannot get to it.

Any ideas? Thanks.

Answered by DTS Engineer in 863406022

Should I be doing as I currently am, where I create modelData as a global let so I can access it everywhere, or should I pass it through the view hierarchy as an Environment variable, like @Environment(ModelData.self) var modelData: ModelData? Bear in mind that some functions are outside of the view hierarchy and cannot access modelData if I do this. Various things like controllers that need access to values in modelData cannot get to it.

Since It's an observable object, make it a State at the App level and pass that down your view hierarchy using environment modifier. You can also have a singleton instance of ModelData if you only ever need a single instance of the object.

We’ll need more information to fully understand the issue. If you encounter the same problem with the relevant code in a small test project, please share a link to it. This will help us better comprehend the situation. If you’re not familiar with creating a test project, refer to Creating a test project.

Accepted Answer

Should I be doing as I currently am, where I create modelData as a global let so I can access it everywhere, or should I pass it through the view hierarchy as an Environment variable, like @Environment(ModelData.self) var modelData: ModelData? Bear in mind that some functions are outside of the view hierarchy and cannot access modelData if I do this. Various things like controllers that need access to values in modelData cannot get to it.

Since It's an observable object, make it a State at the App level and pass that down your view hierarchy using environment modifier. You can also have a singleton instance of ModelData if you only ever need a single instance of the object.

We’ll need more information to fully understand the issue. If you encounter the same problem with the relevant code in a small test project, please share a link to it. This will help us better comprehend the situation. If you’re not familiar with creating a test project, refer to Creating a test project.

Hi, thanks for the response.

I've created a DataService singleton that keeps access to my Core Data store and the model data, which simplifies things a little.

Anywhere I used to use coreData.shared.doSomething() I now use dataService.coreData.doSomething(), and modelData.getSomething() is now dataService.modelData.getSomething() etc., so thanks for that suggestion.

I also now use a @StateObject for the model data at the App level and pass it through the view hierarchy with .environment(modelData), and it seems to work well.

(I had already implemented .environment(modelData) before but it wasn't a @StateObject.)

The disappearing rows issue is now fixed, thanks!

List rows disappearing when scrolling
 
 
Q