I’m working on a SwiftUI application where I have a SessionsView that should display a list of chat sessions. The data for the sessions is fetched from a remote service and stored in an ObservableObject called SessionsViewModel. Despite the data being fetched correctly, the view does not update to reflect the changes.
Here are the relevant parts of my code:
AppDependencies.swift
class AppDependencies: ObservableObject {
static let sessionRepository = FirebaseSessionRepository(firebaseService: FirebaseDatabaseService())
static let userRepository = FirebaseUserRepository(firebaseDatabaseService: FirebaseDatabaseService(), firebaseAuthService: FirebaseAuthService())
let userManager = UserManager(userRepository: AppDependencies.userRepository)
@Published var sessionsViewModel: SessionsViewModel
init() {
sessionsViewModel = AppDependencies.setupSessionsViewModel(userManager: userManager)
}
static func setupSessionsViewModel(userManager: UserManager) -> SessionsViewModel {
let fetchUserSessionsUseCase = FetchUserSessionsUseCase(repository: sessionRepository)
let getUserInfoUseCase = GetUserInfoUseCase(repository: userRepository)
return SessionsViewModel(fetchUserSessionsUseCase: fetchUserSessionsUseCase, getUserInfoUseCase: getUserInfoUseCase, userManager: userManager)
}
}
SessionsView
struct SessionsView: View {
@EnvironmentObject var dependencies: AppDependencies
var body: some View {
NavigationStack {
let sortedSessions = dependencies.sessionsViewModel.sessionsDict.values.sorted { $0.lastUpdated < $1.lastUpdated }
List(sortedSessions) { session in
if let user = dependencies.sessionsViewModel.usersDict[session.id] {
NavigationLink {
ChatView(partnerUser: user, session: session)
.environmentObject(dependencies)
} label: {
let lastUser = session.lastUserId == dependencies.sessionsViewModel.userManager.currentUser?.id ? "You" : user.displayName
VStack(alignment: .leading) {
HStack {
Text("(user.displayName ?? "")")
.font(.headline)
Spacer()
Text("(formattedDate(timestamp: session.lastUpdated))")
.font(.footnote)
.foregroundColor(.gray)
}
Text("(lastUser ?? ""): (session.lastMessage)")
.font(.subheadline)
}
// .padding()
}
.swipeActions(edge: .leading) {
Button("delete", systemImage: "message") {
print("Hi")
}
}
.swipeActions(edge: .trailing) {
Button("Send message", systemImage: "message") {
print("Hi")
}
}
}
}
.navigationTitle("Sessions")
.navigationBarTitleDisplayMode(.inline)
}.onAppear {
self.dependencies.sessionsViewModel.fetchUserSessions(userId: self.dependencies.sessionsViewModel.userManager.currentUser?.id ?? "")
}
}
private func formattedDate(timestamp: Int) -> String {
let date = Date(timeIntervalSince1970: TimeInterval(timestamp) / 1000)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .none
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
}
}
SessionsViewModel
class SessionsViewModel: ObservableObject, ViewModel {
private let createChatSessionUseCase: CreateChatSessionUseCase?
private let fetchUserSessionsUseCase: FetchUserSessionsUseCase?
var userManager: UserManager
@Published var sessionsDict = [String: Session]()
@Published var usersDict = [String: User]()
init(createChatSessionUseCase: CreateChatSessionUseCase? = nil,
fetchUserSessionsUseCase: FetchUserSessionsUseCase? = nil,
userManager: UserManager) {
self.createChatSessionUseCase = createChatSessionUseCase
self.fetchUserSessionsUseCase = fetchUserSessionsUseCase
self.userManager = userManager
}
func createChatSession(partner1: String, partner2: String, completion: @escaping (CreateChatSessionError) -> Void) {
createChatSessionUseCase?.execute(request: (partner1, partner2)) { result in
switch result {
case .success(let session):
break
case .failure(let error):
if let error = error as? CreateChatSessionError {
completion(error)
}
}
}
}
func fetchUserSessions(userId: String) {
fetchUserSessionsUseCase?.execute(request: userId) { result in
switch result {
case .success(let sessionsDict):
self.sessionsDict.merge(sessionsDict) { old, new in new }
for session in sessionsDict.values {
self.getUser(for: session)
}
case .failure(let error):
break
}
}
}
func getUser(for session: Session) {
let partnerId = session.partner1 == userManager.currentUser?.id ? session.partner2 : session.partner1
self.getUserInfoUseCase?.execute(request: partnerId) { result in
switch result {
case .success(let user):
self.usersDict[session.id] = user
case .failure(let error):
// something
break
}
}
}
}
What I’ve Tried:
- Verified that sessionsDict is updated with the correct sessions.
- Verified that SessionsViewModel and AppDependencies both implement ObservableObject.
- Ensured SessionsViewModel uses @Published properties for sessionsDict and usersDict.