SwiftUI View не обновляется

Мое представление не обновляется, когда я изменяю свойство в массиве в классе ObservableObject. Я даже объявил свойство objectWillChange и вызвал его вручную, но View обновлялся случайно или не так, как я хочу. Я этого не понимаю.

import Foundation
import SocketIO
import Combine

class SocketService: ObservableObject {
    static let instance = SocketService()

    let manager = SocketManager(socketURL: URL(RequestURL.base_url)!)
    let socket: SocketIOClient
//    let objectWillChange = ObservableObjectPublisher()

    @Published var allMessages: [Message] = []
//        {
//        willSet {
//            self.objectWillChange.send()
//        }
//    }
    @Published var writtenUsers: [PreviewMessage] = []
//        {
//        willSet {
//            self.objectWillChange.send()
//        }
//    }

    init() {
        socket = manager.defaultSocket
        setSocketEvents()
    }

    // This method is called
    func recieve_read_all_messages() {
        socket.on("recieve-read-all-messages") { (data, ack) in
            guard let arr = data as? [[String: Any]] else { return }
            guard let userID = arr[0]["userID"] as? Int else {
                print("no userID, \(#function), line: \(#line)"); return
            }

            // self.objectWillChange.send()
            for msg in self.allMessages {
                // Here im trying to change the property in the array
                msg.content.readStatus = .read
            }
    }    
}

Даже когда я напрямую меняю это свойство, представление не обновляется.

@EnvironmentObject private var socketService: SocketService

var body: some View {
    VStack {
         List(filteredMessages, id: \.content.uuid, rowContent: chatSpeechBubbleView)
         sendView
    }
}

private func chatSpeechBubbleView(forMessage message: Message) -> some View {
        ChatSpeechBubble(message: message)
    }

private var sendView: some View {
        Button(action: sendMessage) {
            SFSymbol(.paperplane_fill)
                .fontSize(20)
                .foregroundColor(.white)
                .rotate(.degrees(45))
                .padding(10)
                .padding(.trailing, 5)
                .backgroundColor(.blue)
                .clipCircle()
        }
        .padding(.bottom, 2)
    }

    func sendMessage() {
        for msg in socketService.allMessages {            
            msg.content.readStatus = .read
        }
    }

Мое другое мнение, где его следует обновить:

struct DoubleCheckmark: View {
    var messageContent: MessageContent

    var body: some View {
        HStack(spacing: 0) {
            SFSymbol(.checkmark)
                .resizable()
                .scaledToFit()
            SFSymbol(.checkmark)
                .resizable()
                .scaledToFit()
                .padding(.leading, -9)
        }
        .height(13)
        .foregroundColor(self.messageContent.readStatus == .read ? .blue : .gray)
    }
}
struct ChatSpeechBubble: View {

    // MARK: - init variables
    var message: Message

    // MARK: - normal variables
    var ownSendMessage: Bool {
        message.fromUser.id == UDService.shared.user.id
    }

    // MARK: - Body
    var body: some View {
        messageContent
    }

    private var messageContent: some View {
        HStack(alignment: .bottom) {
            if message.content.text != nil {
                Text(message.content.text!)
                    .foregroundColor(.black)
            }
            if message.content.imageURL != nil {
                Spacer(minLength: 0)
            }
            Text(message.content.timeHourMinute)
                .font(.caption)
                .foregroundColor(.gray)

            if ownSendMessage {
                DoubleCheckmark(messageContent: self.message.content)
            }
        }
    }
}

person SwiftiSwift    schedule 24.12.2019    source источник


Ответы (1)


Используйте struct, а не class

Проблема в том, что Message и его подтипы следует объявлять как struct, а не как class. Вот почему.

Вот простой пример, который я сделал, чтобы продемонстрировать разницу:

Пользователь как struct:  Пользователь как структура

Пользователь как class:  Пользователь как класс

Посмотрите журналы печати внизу. С struct снова печатается "Body", но почему?

Это потому, что классы передаются по ссылке, а структуры передаются по значению. Это означает, что вы можете изменять свойства экземпляра класса, например:

Пример SpriteKit

Если бы SKSpriteNode был struct со свойствами let, вы бы получили сообщение об ошибке при попытке изменить константу let:

Cannot assign to property: '*' is a 'let' constant

Заключение

Таким образом, поскольку Message является class, экземпляр не меняется. Но с struct все меняется. Вам нужно изменить все это (используя struct), чтобы @Published & ObservableObject мог наблюдать за изменением.

person George_E    schedule 24.12.2019