-1

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.

2
  • That error is exactly what it says somewhere you have a @StateObject and it is not in a View it doesn't seem to be here since you only have 1 and it is in MessageView Commented Dec 17, 2023 at 19:42
  • I see you have not taken my advice to your previous question when you used another fake account: stackoverflow.com/questions/77671221/… and a different name saladboi. Have a look at this official link, it gives you some good examples of how to manage data in your app: monitoring data Commented Dec 17, 2023 at 23:32

1 Answer 1

0

You shouldn't init objects like the SocketManager inside a View struct and you can't use DispatchQueue.main.async in View structs either. View data structs are just simple values that are generated, differed and destroyed. So if you create objects in them it's considered a memory leak and a dispatch async will return onto some old lost View struct if it does at all.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! I wrote a separate controller to initialize SocketManager and things worked out.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.