'SwiftUI can not add onChange event

I have a view in which I display a list of my messages. Whenever a new message is added I want to scroll to the bottom. I am trying to add an onChange event to the ForEach but this breaks my code with some weird errors:

Referencing instance method 'onChange(of:perform:)' on 'Array' requires that 'Message' conform to 'Equatable'

and sometimes

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

When I remove onChange everything complies.

Here is the Message model:

struct Message: Identifiable {
    var id = UUID()
    var text: String
    var createdAt: Date = Date()
    var senderID: String
    var seen: Bool
    
    init(dictionary: [String: Any]) {
        self.text = dictionary["text"] as? String ?? ""
        self.senderID = dictionary["senderID"] as? String ?? ""
        self.seen = dictionary["seen"] as? Bool ?? false
    }
}

What am I doing wrong here?

`ForEach

struct MessagesView: View {
    @ObservedObject var messagesViewModel: MessagesViewModel
    
    var body: some View {
        VStack {
            ScrollView(.vertical, showsIndicators: false, content: {
                ScrollViewReader { reader in
                    VStack(spacing: 20) {
                        ForEach(messagesViewModel.request.messages) { message in
                            Text(message.text)
                                .id(message.id)
                        }
                        .onChange(of: messagesViewModel.request.messages) { (value) in
                            reader.scrollTo(value.last?.id)
                        }
                    }
                }
            })
        }
    }
}


Solution 1:[1]

If your messages is a @Published the following should work (as I tested in Make a list scroll to bottom with SwiftUI)

struct MessagesView: View {
    @ObservedObject var messagesViewModel: MessagesViewModel
    
    var body: some View {
        VStack {
            ScrollView(.vertical, showsIndicators: false, content: {
                ScrollViewReader { reader in
                    VStack(spacing: 20) {
                        ForEach(messagesViewModel.request.messages) { message in
                            Text(message.text)
                                .id(message.id)
                        }
                        .onReceive(messagesViewModel.request.$messages) { (value) in
                           guard !value.isEmpty else { return } 
                           reader.scrollTo(value.last!.id)
                        }
                    }
                }
            })
        }
    }
}

backup

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1