Reputation: 680
Here's my model:
struct ChatRoom: Identifiable, Equatable {
static func == (lhs: ChatRoom, rhs: ChatRoom) -> Bool {
lhs.id == rhs.id
}
struct LastMessage: Codable {
let isSeen: Bool
var isOfCurrentUser: Bool?
let createdAt: Date
let senderId, text: String
}
let id: String
let userIds: [String]
var lastMessage: LastMessage
let otherUser: User
let currentUserAvatarObject: [String : Any]?
let isTyping: Bool
var blockerIds: [String]
let archiverIds: [String]
let messages: Int
let senderMessages: Int
let receiverMessages: Int
}
I have the ChatRoomsListView
view which initializes its ViewModel
like so:
struct ChatRoomsListView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
VStack {
HStack {
Button {
} label: {
Image(systemName: "camera")
.scaledToFit()
.foregroundColor(.white)
.frame(width: 30, height: 30)
.padding(6)
.background(
Circle().foregroundColor(.white.opacity(0.15))
)
}
Spacer()
Text("Chats")
.foregroundColor(.white)
.offset(x: -20)
Spacer()
}
ScrollView(.vertical) {
LazyVStack {
ForEach(viewModel.chatRooms) { chatRoom in
ChatRoomView(chatRoom: chatRoom)
}
}
}
}.padding(.vertical, 44)
if viewModel.chatRooms.isEmpty {
Text("It seems you haven’t chatted with anyone yet! That’s ok!")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.horizontal)
}
}
}
}
private struct ChatRoomView: View {
let chatRoom: ChatRoom
private var text: String {
chatRoom.isTyping ? NSLocalizedString("Typing...", comment: "") : chatRoom.lastMessage.text
}
private var hasUnseenMessage: Bool {
!chatRoom.lastMessage.isSeen && chatRoom.lastMessage.isOfCurrentUser == false
}
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
VStack(spacing: 0) {
HStack {
if hasUnseenMessage {
Circle()
.frame(width: 10, height: 10)
.foregroundColor(.blue)
}
VStack(alignment: .leading) {
Text(chatRoom.otherUser.username)
.foregroundColor(.white)
Text(text)
.foregroundColor(.white)
.lineLimit(1)
}
Spacer()
if chatRoom.lastMessage.isSeen && chatRoom.lastMessage.isOfCurrentUser == true {
Image(systemName: "checkmark.circle")
.foregroundColor(.white)
}
}.padding()
}
}
}
}
And here’s the ViewModel
:
extension ChatRoomsListView {
class ViewModel: ObservableObject {
@Published var chatRooms = [ChatRoom]()
// -----------------------------
@Injected private var chatRoomsService: ChatRoomsRepository
@Injected private var currentUserService: CurrentUserRepository
// -----------------------------
init() {
getChatRooms()
subscribeToChatRoomUpdates()
}
private func getChatRooms() {
guard let currentUser = currentUserService.user else { return }
chatRoomsService.getChatRooms(currentUser: currentUser) { [weak self] chatRooms in
self?.chatRooms = chatRooms
}
}
private func subscribeToChatRoomUpdates() {
guard let currentUser = currentUserService.user else { return }
chatRoomsService.subscribeToChatRoomUpdates(currentUser: currentUser) { [weak self] chatRooms in
DispatchQueue.main.async {
for chatRoom in chatRooms {
if let index = self?.chatRooms.firstIndex(where: { $0.id == chatRoom.id }) {
self?.chatRooms[index] = chatRoom
} else {
self?.chatRooms.insert(chatRoom, at: 0)
}
}
self?.chatRooms.removeAll(where: { $0.archiverIds.contains(currentUser.id) })
self?.chatRooms.sort(by: { $0.lastMessage.createdAt > $1.lastMessage.createdAt })
}
}
}
}
}
My problem is that once the getChatRooms
is called and changes the chatRooms
array for the first time, after that, every time the subscribeToChatRoomUpdates
is called doesn't redraw the ChatRoomView
child view. On the other hand, ChatRoomsListView
gets updated properly.
Why is that happening?
Upvotes: 2
Views: 161
Reputation: 30571
I think your implementation of Equatable
- where it only compares id
has broken the ability for the ChatView
to detect a change to the let chatRoom: ChatRoom
, so SwiftUI doesn't call body
because the ids are unchanged between the old value and the new value. Try removing Equatable
.
By the way, you need to unsubscribe to updates in the object's deinit
or you might get a crash. And your use of self?
is problematic, look into retain cycles in blocks.
Upvotes: 2