I'm fairly new to SwiftUI, and I'm trying to build a chatroom. When I send message to the app, the new message won't display and I got a warning in the terminal saying "Accessing StateObject's object without being installed on a View. This will create a new instance each time."
import SwiftUI
import SocketIO
struct ChatMessage: Hashable {
var message: String
var MatchImg: String
var isMe: Bool
}
class ChatMessageList: ObservableObject {
@Published var list: [ChatMessage]
init(list: [ChatMessage]) {
self.list = list
}
func addItem(item: ChatMessage) {
list.append(item)
objectWillChange.send()
}
}
struct MessageView: View {
@State private var showMenu: Bool = false
@StateObject var messages = ChatMessageList(list: [
ChatMessage(message: "Hello world", MatchImg:"ye",isMe: true),
ChatMessage(message: "Hello world", MatchImg:"ye",isMe: false)
])
@State private var composedMessage: String = ""
let roomID: String
let userID: String
let otherUser: String
private var socket: SocketIOClient
private var manager: SocketManager
init(roomID: String, userID: String, otherUser: String) {
self.roomID = roomID
self.userID = userID
self.otherUser = otherUser
self.manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [.log(false), .compress])
self.socket = self.manager.defaultSocket
self.socket.connect()
self.addHandlers()
}
func addHandlers() {
self.socket.on("chat message") { data, ack in
let messageText = data[0]
let newMessage = ChatMessage(message: messageText as! String, MatchImg: "ye", isMe: false)
addMessage(message: newMessage)
// DispatchQueue.main.async {
// self.messages.list.append(newMessage)
// }
}
}
func addMessage(message: ChatMessage) {
DispatchQueue.main.async {
messages.addItem(item: message)
}
}
var body: some View {
VStack {
VStack(alignment: .center){
//omitted
}
List{
ForEach(messages.list, id: \.self) { msg in
ChatRow(chatMessage: msg)
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
}
}
.padding(0)
.listStyle(PlainListStyle())
.environment(\.defaultMinListRowHeight, 0)
HStack {
TextField("Message...", text: $composedMessage).frame(minHeight: CGFloat(36))
.padding(.horizontal,12)
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(Color.blue, lineWidth: 2)
)
Button("Send"){
Task {
self.socket.emit("message", roomID, composedMessage)
self.messages.addItem(item: ChatMessage(message: composedMessage, MatchImg: "ye", isMe: true))
print(self.messages)
self.composedMessage = ""
}
}
Button("add") {
showMenu.toggle()
}
Button(action: {}){
Image("person.fill")
.foregroundColor(Color.black)
}
}.padding(20)
.task{
print(self.socket.status)
self.socket.emit("identify", userID)
self.socket.emit("subscribe", roomID, otherUser)
}
}
}
}
}
struct ChatRow : View {
var chatMessage: ChatMessage
var body: some View {
if !chatMessage.isMe {
HStack {
// omitted
}
}
else {
// omitted
}
}
}
I've tried
- Using
DispatchQueue.main.async - Write a separate function to add new messages (
addMessage()) - Write a function in the class
ChatMessageList
I printed the state object after adding a new item, but there's no new message. But all the above functions seems to work just fine when sending new message.
@StateObjectand it is not in aViewit doesn't seem to be here since you only have 1 and it is inMessageViewsaladboi. Have a look at this official link, it gives you some good examples of how to manage data in your app: monitoring data