質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.81%

MessageKitで「Invalid update: invalid number of sections. 」エラーが出てしまう

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 316

yotubarail

score 21

MessageKitとFirestoreを用いてチャットアプリを作ってみようと考えています。
Firestoreとデータを送受信できるようにしたいのですが、Firestoreにデータを送ったあと「Invalid update: invalid number of sections. 」とエラーが出てしまいました。
sendMessageToFirebase(text: text)をコメントアウトし、Firestoreにデータを送信しないようにするとmessageInputBarに入力した文字が画面上に表示されます。
UICollectionViewのセクション数の問題であるのはわかるのですが、Firestoreに送るかどうかでなぜこのような違いが出ているのかわからずにいます。

解決の糸口となりそうなものをお分かりになる方がいらっしゃいましたら、ご教授頂けると幸いです。

class ViewController: MessagesViewController {

    let chatCnt: Int = 100
    var messageList: [MockMessage] = []

    var database: Firestore!


    let dateFormatter:DateFormatter = DateFormatter()              //日時のフォーマットを管理するもの

    lazy var formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        return formatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        print("App open")

        database = Firestore.firestore()



        //Firestoreからデータを取得し、TextViewに表示する
        self.database.collection("chat").order(by: "timestamp", descending: true).limit(to: self.chatCnt + 25).addSnapshotListener { (snapShot, error) in
            guard snapShot != nil else {
                print("snapShot is nil")
                return
            }

            snapShot!.documentChanges.forEach{diff in
                //更新内容が追加だったときの処理
                if diff.type == .added {
                    //追加データを変数に入れる
                    let chatDataOp = diff.document.data() as? Dictionary<String, String>
                    print(diff.document.data())
                    guard let chatData = chatDataOp else {
                        return
                    }
                    guard chatData["content"] != nil else {
                        return
                    }
                    guard chatData["senderName"] != nil else {
                        return
                    }
                    //TextViewの一番下に新しいメッセージ内容を追加する
                    self.messageList =  self.getMessages()
                }
            }
        }


        DispatchQueue.main.async {
            // messageListにメッセージの配列をいれて
            self.messageList = self.getMessages()
            // messagesCollectionViewをリロードして
            self.messagesCollectionView.reloadData()
            // 一番下までスクロールする
            self.messagesCollectionView.scrollToBottom()
        }

        messagesCollectionView.messagesDataSource = self
        messagesCollectionView.messagesLayoutDelegate = self
        messagesCollectionView.messagesDisplayDelegate = self
        messagesCollectionView.messageCellDelegate = self

        messageInputBar.delegate = self
        messageInputBar.sendButton.tintColor = UIColor.lightGray



        // メッセージ入力時に一番下までスクロール
        scrollsToBottomOnKeyboardBeginsEditing = true // default false
        maintainPositionOnKeyboardFrameChanged = true // default false

        //DateFormatter()で日付と時刻と地域を指定(今回は日本時間を指定)
        dateFormatter.dateStyle = .medium //日付の表示スタイルを決定
        dateFormatter.timeStyle = .short  //時刻の表示スタイルを決定
        dateFormatter.locale = Locale(identifier: "ja_JP")//地域を決定
    }





    //Cloud Firestoreに書き込みをする際の処理
    //Firebaseにチャット内容を保存するためのメソッド
    //Firebaseに送りたい情報は今回はテキスト
    func sendMessageToFirebase(text: String){
        if !messageList.isEmpty {messageList = [] } //辞書の初期化(送信データの中身がからじゃなければ空にする)

        //これがJSON(書き方のルール的な)
        _ = database.collection("message").addDocument(data: [
            "senderName": "takinami",//送信者の名前
            "content": text,                //送信内容(今回は文字のみ)

            "createdAt": dateFormatter.string(from: Date()),//送信時刻
            "messageId": messageList //送信メッセージのID
            ])

    }

    // サンプル用に適当なメッセージ
    func getMessages() -> [MockMessage] {
        return [
            createMessage(text: "String")
        ]
    }

    func createMessage(text: String) -> MockMessage {
        let attributedText = NSAttributedString(string: text, attributes: [.font: UIFont.systemFont(ofSize: 15),
                                                                           .foregroundColor: UIColor.black])
        return MockMessage(attributedText: attributedText, sender: otherSender(), messageId: UUID().uuidString, date: Date())
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

extension ViewController: MessagesDataSource {

    func currentSender() -> SenderType {
        return Sender(id: "123", displayName: "自分")
    }

    func otherSender() -> Sender {
        return Sender(id: "456", displayName: "知らない人")
    }

    func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int {
        return messageList.count
    }

    func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType {
        return messageList[indexPath.section]
    }

    // メッセージの上に文字を表示
    func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
        if indexPath.section % 3 == 0 {
            return NSAttributedString(
                string: MessageKitDateFormatter.shared.string(from: message.sentDate),
                attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10),
                             NSAttributedString.Key.foregroundColor: UIColor.darkGray]
            )
        }
        return nil
    }

    // メッセージの上に文字を表示(名前)
    func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
        let name = message.sender.displayName
        return NSAttributedString(string: name, attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
    }

    // メッセージの下に文字を表示(日付)
    func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
        let dateString = formatter.string(from: message.sentDate)
        return NSAttributedString(string: dateString, attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption2)])
    }
}

// メッセージのdelegate
extension ViewController: MessagesDisplayDelegate {

    // メッセージの色を変更(デフォルトは自分:白、相手:黒)
    func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
        return isFromCurrentSender(message: message) ? .white : .darkText
    }

    // メッセージの背景色を変更している(デフォルトは自分:緑、相手:グレー)
    func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
        return isFromCurrentSender(message: message) ?
            UIColor(red: 69/255, green: 193/255, blue: 89/255, alpha: 1) :
            UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1)
    }

    // メッセージの枠にしっぽを付ける
    func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageStyle {
        let corner: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
        return .bubbleTail(corner, .curved)
    }

    // アイコンをセット
    func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
        // message.sender.displayNameとかで送信者の名前を取得できるので
        // そこからイニシャルを生成するとよい
        let avatar = Avatar(initials: "人")
        avatarView.set(avatar: avatar)
    }
}


// 各ラベルの高さを設定(デフォルト0なので必須)
extension ViewController: MessagesLayoutDelegate {

    func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
        if indexPath.section % 3 == 0 { return 10 }
        return 0
    }

    func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
        return 16
    }

    func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
        return 16
    }
}

extension ViewController: MessageCellDelegate {
    // メッセージをタップした時の挙動
    func didTapMessage(in cell: MessageCollectionViewCell) {
        print("Message tapped")
    }
}

extension ViewController: MessageInputBarDelegate {
    // メッセージ送信ボタンをタップした時の挙動
    func inputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {


        for _ in inputBar.inputTextView.components {

        let attributedText = NSAttributedString(string: text, attributes: [.font: UIFont.systemFont(ofSize: 15),
                                                                                          .foregroundColor: UIColor.white])
        let message = MockMessage(attributedText: attributedText, sender: currentSender(), messageId: UUID().uuidString, date: Date())
        messageList.append(message)
        messagesCollectionView.insertSections([messageList.count - 1])
        }



        //Firebaseに送信するメソッド
        sendMessageToFirebase(text: text)


        //inputBarの中のテキストを表示して
        inputBar.inputTextView.text = String()


        //一番下までスクロールしている
        messagesCollectionView.scrollToBottom()

        print("messageList when sendButton pressed:\(messageList)")
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

firebaseのsnapShotを利用しているのに、手動でもmessageListにtextを追加しているから?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/11/18 16:48

    回答ありがとうございます。
    おそらくおっしゃる通りなのですが、func getMessages()をどのように変えれば良いのかをわかっていないため自分で色々試してみようと思います。

    キャンセル

  • 2019/11/18 17:10

    firebase は「リアルタイム」データベース 👍
    https://firebase.google.com/docs/database/ios/read-and-write?hl=ja

    キャンセル

  • 2019/11/18 17:14

    ありがとうございます!

    キャンセル

0

if !messageList.isEmpty {messageList = [] }

が入っているからでは???

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/11/13 17:18

    回答ありがとうございます。
    該当部分をコメントアウトし、再チャレンジしたところ
    「'Unsupported type: __SwiftValue'」エラーが出てしまいました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る