My apps data workflow is causing the UI to be very buggy when fetching more data which causes me to wonder the best practice for storing/displaying/fetching data, the setup I have works as follow:
The very top view in the hierarchy stores classes @StateObject var messageViewModel = MessageViewModel()
that contain @Published arrays of data. Then the class is injected into the main ContentView via envirnmentObject .environmentObject(messageViewModel)
. To access the classes data in a subview I use @EnvironmentObject var viewModel: messageViewModel
. I use this EnvironmentObject viewModel in the subview to store the users data in @Published variables. When the user navigates to someone’s profile, my backend fetches the data and inserts it into the @Published array. My subviews directly access the @Published data like so viewModel.users
and present it inside a scrollview for example using a ForEach.
I don’t believe this approach is optimal since my UI gets buggy when data is inserted into these @Published properties. It cases my animations to stop working for a second. I’m wondering how I could refactor my setup so that data is handled in a proper manner so that my animations work when transitioning to a new view that caches data.
import SwiftUI
import Combine
// ViewModel with @Published array of data
class MessageViewModel: ObservableObject {
@Published var users: [User] = []
// Simulate fetching data from backend
func fetchMoreUsers() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let newUsers = [
User(id: UUID(), name: "John Doe"),
User(id: UUID(), name: "Jane Smith")
]
self.users.append(contentsOf: newUsers)
}
}
}
struct User: Identifiable {
let id: UUID
let name: String
}
@main
struct MyApp: App {
@StateObject var messageViewModel = MessageViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(messageViewModel)
//My app has about 20 .environmentObject()'s
}
}
}
struct ContentView: View {
@EnvironmentObject var viewModel: MessageViewModel
var body: some View {
NavigationStack {
VStack {
NavigationLink(destination: UserProfileView()) {
Text("Go to Profile")
}
.padding()
Button(action: {
viewModel.fetchMoreUsers()
}) {
Text("Fetch More Users")
}
}
}
}
}
struct UserProfileView: View {
@EnvironmentObject var viewModel: MessageViewModel
var body: some View {
ScrollView {
ForEach(viewModel.users) { user in
Text(user.name)
.padding()
}
}
.onAppear {
// Simulate initial data fetch
if viewModel.users.isEmpty {
viewModel.fetchMoreUsers()
}
}
}
}
3