Как хранить значения, ссылаясь на классы в FireStore - PullRequest
0 голосов
/ 12 декабря 2018

Я создаю приложение чата с использованием библиотеки MessageKit в Swift 4.2 и FireStore.Моя проблема в том, что я не могу сохранить данные, полученные в режиме реального времени с использованием addSnapshotListener в классе Message и подтвердившее содержание класса, но я не думаю, что это неправильно.

messageListener работает правильно.Когда выполняется handleDocumentChange, возвращается nil.

Я проверил возвращаемое значение с помощью document.data (), но значение вернулось.Как хранить значения, ссылаясь на классы?

guard var message = Message (document: change.document) else {
     print ("return Message")
    return
} 

Данные, введенные для FireStore, следующие:

{
  "channels": [{
    "MOuL1sdbrnh0x1zGuXn7": { // channel id
    "name": "Puppies",
        "thread": [{
        "3a6Fo5rrUcBqhUJcLsP0": { // message id
        "content": "Wow, that's so cute!",
        "created": "May 12, 2018 at 10:44:11 PM UTC-5",
        "senderID": "YCrPJF3shzWSHagmr0Zl2WZFBgT2",
        "senderUsername": "naturaln0va",
        "recipientProfilePictureURL":"URL", 
       "recipientID":"ezample", 
        "recipientUsername" :"A"
        "recipientProfilePictureURL":"aaaaa"
       },
    }]
  },
}]
}

Это мой класс сообщений:

class Message: MessageType {
    var id: String?
    var sentDate: Date
    var kind: MessageKind

    lazy var sender: Sender = Sender(id: atcSender.uid ?? "No Id", displayName: atcSender.uid ?? "No Name")

    var atcSender: User
    var recipient: User

    var messageId: String {
        return id ?? UUID().uuidString
    }

    var image: UIImage? = nil
    var downloadURL: URL? = nil
    let content: String

    init(messageId: String, messageKind: MessageKind, createdAt: Date, atcSender: User, recipient: User) {
        self.id = messageId
        self.kind = messageKind
        self.sentDate = createdAt
        self.atcSender = atcSender
        self.recipient = recipient

        switch messageKind {
        case .text(let text):
            self.content = text
        default:
            self.content = ""
        }
    }

    init(user: User, image: UIImage) {
        self.image = image
        content = ""
        sentDate = Date()
        id = nil
        self.kind = MessageKind.text("xxx")
        self.atcSender = user
        self.recipient = user
    }

    init?(document: QueryDocumentSnapshot) {
        let data = document.data()

        guard let sentDate = data["created"] as? Date else {
            return nil
        }
        guard let senderID = data["senderID"] as? String else {
            return nil
        }
        guard let senderUsername = data["senderUsername"] as? String else {
            return nil
        }
        guard let senderProfilePictureURL = data["senderProfilePictureURL"] as? String else {
            return nil
        }
        guard let recipientID = data["recipientID"] as? String else {
            return nil
        }
        guard let recipientUsername = data["recipientUsername"] as? String else {
            return nil
        }
        guard let recipientProfilePictureURL = data["recipientProfilePictureURL"] as? String else {
            return nil
        }

        id = document.documentID
        self.sentDate = sentDate
        self.atcSender = User(uid: senderID, username: senderUsername, firstname: "", lastname: "", email: "", profileUrl: senderProfilePictureURL)
        self.recipient = User(uid: recipientID, username: recipientUsername, firstname: "", lastname: "", email: "", profileUrl: recipientProfilePictureURL)

        if let content = data["content"] as? String {
            self.content = content
            downloadURL = nil
        } else if let urlString = data["url"] as? String, let url = URL(string: urlString) {
            downloadURL = url
            self.content = ""
        } else {
            return nil
        }
        self.kind = MessageKind.text(content)
    }

    required init(jsonDict: [String: Any]) {
        fatalError()
    }

    var description: String {
        return self.messageText
    }

    var messageText: String {
        switch kind {
        case .text(let text):
            return text
        default:
            return ""
        }
    }

    var channelId: String {
        let id1 = (recipient.username ?? "")
        let id2 = (atcSender.username ?? "")
        return id1 < id2 ? id1 + id2 : id2 + id1
    }
}

extension Message: DatabaseRepresentation {
    var representation: [String : Any] {
        var rep: [String : Any] = [
            "created": sentDate,
            "senderID": atcSender.uid ?? "",
            "senderUsername": atcSender.username ?? "",
            "senderProfilePictureURL": atcSender.profileUrl ?? "",
            "recipientID": recipient.uid ?? "",
            "recipientUsername": recipient.username ?? "",
            "recipientProfilePictureURL": recipient.profileUrl ?? "",
            ]

        if let url = downloadURL {
            rep["url"] = url.absoluteString
        } else {
            rep["content"] = content
        }
        return rep
    }}extension Message: Comparable {

    static func == (lhs: Message, rhs: Message) -> Bool {
        return lhs.id == rhs.id
    }
    static func < (lhs: Message, rhs: Message) -> Bool {
        return lhs.sentDate < rhs.sentDate
    }
}

ChatViewController:

import UIKit
import MessageKit
import MessageInputBar
import Firebase
import FirebaseFirestore
import FirebaseAuth

class ChatViewController: MessagesViewController {
    private let db = Firestore.firestore()
    private var reference: CollectionReference?

    private var messages: [Message] = []
    private var messageListener: ListenerRegistration?

    private let user: User
    private let channel: Channel

    let uid = Auth.auth().currentUser?.uid

    init(user: User, channel: Channel) {
        self.user = user
        self.channel = channel
        super.init(nibName: nil, bundle: nil)

        title = channel.name
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {
        messageListener?.remove()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let id = channel.id else {
            navigationController?.popViewController(animated: true)
            return
        }

        reference = db.collection(["channels", id, "thread"].joined(separator: "/"))


     //            reference?.addSnapshotListener { querySnapshot, error in
     //                guard let snapshot = querySnapshot else {
     //                    print("Error fetching snapshots: \(error!)")
     //                    return
     //                }
     //                snapshot.documentChanges.forEach { diff in
     //                    if (diff.type == .added) {
     //                        print("New city: \(diff.document.data())")
     //                    }
     //                    if (diff.type == .modified) {
     //                        print("Modified city: \(diff.document.data())")
     //                    }
     //                    if (diff.type == .removed) {
     //                        print("Removed city: \(diff.document.data())")
     //                    }
     //                }
     //        }

        messageListener = reference?.addSnapshotListener { querySnapshot, error in
            guard let snapshot = querySnapshot else {
                print("Error listening for channel updates: \(error?.localizedDescription ?? "No error")")
                return
            }

            snapshot.documentChanges.forEach { change in
                self.handleDocumentChange(change)
                 print("handleDocumentChange")
            }
        }

        self.navigationItem.title = title

        messageInputBar.delegate = self
        messagesCollectionView.messagesDataSource = self
        messagesCollectionView.messagesLayoutDelegate = self
        messagesCollectionView.messagesDisplayDelegate = self
        messageInputBar.sendButton.tintColor = UIColor.lightGray

        //scrollsToBottomOnKeyboardBeginsEditing = true // default false
        //maintainPositionOnKeyboardFrameChanged = true // default false
    }

    private func save(_ message: Message) {
        reference?.addDocument(data: message.representation) { error in
            if let e = error {
                print("Error sending message: \(e.localizedDescription)")
                return
            }
            self.messagesCollectionView.scrollToBottom()
        }
    }

    private func insertNewMessage(_ message: Message) {
        guard !messages.contains(message) else {
            return
        }

        messages.append(message)
        messages.sort()

        let isLatestMessage = messages.index(of: message) == (messages.count - 1)
        let shouldScrollToBottom = messagesCollectionView.isAtBottom && isLatestMessage

        messagesCollectionView.reloadData()

        if shouldScrollToBottom {
            DispatchQueue.main.async {
                self.messagesCollectionView.scrollToBottom(animated: true)
            }
        }
    }

    private func handleDocumentChange(_ change: DocumentChange) {
        guard var message = Message(document: change.document) else {
             print("return Message")
            return
        }

        switch change.type {
        case .added:
            print("add Message")
            insertNewMessage(message)
        default:
            break
        }
    }
}

Отпечатки консоли

New city: ["senderUsername": panyayan, 
           "senderID": RAMIqHAVeoU4TKkm3FDw7XUwgym2, 
           "created": FIRTimestamp:seconds=1544623185 nanoseconds=412169933>, 
           "recipientUsername": panyayan, 
           "content": AAA, 
           "recipientID": RAMIqHAVeoU4TKkm3FDw7XUwgym2, 
           "recipientProfilePictureURL": https:, 
           "senderProfilePictureURL": https:]
           return Message
           handleDocumentChange

1 Ответ

0 голосов
/ 16 января 2019

Не знаю, нужно ли вам это, но: document.data() поле created является FIRTimestamp.Когда вы пытаетесь инициализировать объект Message, который вы используете

guard let sentDate = data["created"] as? Date else {
    return nil
}

Это может быть причиной того, почему ваш объект равен нулю.Попробуйте что-то вроде

guard let sentTimestamp = data["created"] as? Timestamp else {
    return nil
}
...              
self.sentDate = sentTimestamp.dateValue()
...