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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

structure

このタグは、プログラム言語におけるデータ型structure(構造体)に関するタグです。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

0回答

1830閲覧

Swift4 &FireStore:addSnapshotListenerでStructにマッピングできない

panyayan

総合スコア36

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

structure

このタグは、プログラム言語におけるデータ型structure(構造体)に関するタグです。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2018/12/11 10:59

現在MessageKitというライブラリーを使用してチャットアプリを作成しています。
チャットのやり取りをするためのチャンネルコレクションを作成しチャット画面に遷移、テキストを入力すると
構造体で保存されるように機能を作成しました。

JSON

1{ 2 "channels": [{ 3 "MOuL1sdbrnh0x1zGuXn7": { // channel id 4 "name": "Puppies", 5 "thread": [{ 6 "3a6Fo5rrUcBqhUJcLsP0": { // message id 7 "content": "Wow, that's so cute!", 8 "created": "May 12, 2018 at 10:44:11 PM UTC-5", 9 "senderID": "YCrPJF3shzWSHagmr0Zl2WZFBgT2", 10 "senderUsername": "naturaln0va", 11 "recipientProfilePictureURL":"URL", 12      "recipientID":"ezample", 13 "recipientUsername" :"A" 14 "recipientProfilePictureURL":"aaaaa" 15 }, 16 }] 17 }, 18 }] 19}

実際にテキストを入力すると、FIrestoreの方に値が保存されます。
そして、リアルタイムで更新されたデータを取得するべくaddSnapshotListenerを使用して、ドキュメントに動きがあった際、
それに合わせて Messageという構造体にマッピングしあらかじめ用意しておいたArrayに格納するというコードを書いたのですが、値がマッピングされません。
アドバイスいただけないでしょうか

環境
Swift4.2
MessageKit
Firestore

Message

1import Firebase 2import MessageKit 3import FirebaseFirestore 4 5class Message: MessageType { 6 var id: String? 7 var sentDate: Date 8 var kind: MessageKind 9 10 lazy var sender: Sender = Sender(id: atcSender.uid ?? "No Id", displayName: atcSender.uid ?? "No Name") 11 12 var atcSender: User 13 var recipient: User 14 15 var messageId: String { 16 return id ?? UUID().uuidString 17 } 18 19 var image: UIImage? = nil 20 var downloadURL: URL? = nil 21 let content: String 22 23 init(messageId: String, messageKind: MessageKind, createdAt: Date, atcSender: User, recipient: User) { 24 self.id = messageId 25 self.kind = messageKind 26 self.sentDate = createdAt 27 self.atcSender = atcSender 28 self.recipient = recipient 29 30 switch messageKind { 31 case .text(let text): 32 self.content = text 33 default: 34 self.content = "" 35 } 36 } 37 38 init(user: User, image: UIImage) { 39 self.image = image 40 content = "" 41 sentDate = Date() 42 id = nil 43 self.kind = MessageKind.text("xxx") 44 self.atcSender = user 45 self.recipient = user 46 } 47 48 init?(document: QueryDocumentSnapshot) { 49 let data = document.data() 50 guard let sentDate = data["created"] as? Date else { 51 return nil 52 } 53 guard let senderID = data["senderID"] as? String else { 54 return nil 55 } 56 guard let senderUsername = data["senderUsername"] as? String else { 57 return nil 58 } 59 guard let senderProfilePictureURL = data["senderProfilePictureURL"] as? String else { 60 return nil 61 } 62 guard let recipientID = data["recipientID"] as? String else { 63 return nil 64 } 65 guard let recipientUsername = data["recipientUsername"] as? String else { 66 return nil 67 } 68 guard let recipientProfilePictureURL = data["recipientProfilePictureURL"] as? String else { 69 return nil 70 } 71 72 id = document.documentID 73 74 self.sentDate = sentDate 75 self.atcSender = User(uid: senderID, username: senderUsername, firstname: "", lastname: "", email: "", profileUrl: senderProfilePictureURL) 76 self.recipient = User(uid: recipientID, username: recipientUsername, firstname: "", lastname: "", email: "", profileUrl: recipientProfilePictureURL) 77 78 if let content = data["content"] as? String { 79 self.content = content 80 downloadURL = nil 81 } else if let urlString = data["url"] as? String, let url = URL(string: urlString) { 82 downloadURL = url 83 self.content = "" 84 } else { 85 return nil 86 } 87 self.kind = MessageKind.text(content) 88 } 89 90 required init(jsonDict: [String: Any]) { 91 fatalError() 92 } 93 94 var description: String { 95 return self.messageText 96 } 97 98 var messageText: String { 99 switch kind { 100 case .text(let text): 101 return text 102 default: 103 return "" 104 } 105 } 106 107 var channelId: String { 108 let id1 = (recipient.username ?? "") 109 let id2 = (atcSender.username ?? "") 110 return id1 < id2 ? id1 + id2 : id2 + id1 111 } 112} 113 114extension Message: DatabaseRepresentation { 115 116 var representation: [String : Any] { 117 var rep: [String : Any] = [ 118 "created": sentDate, 119 "senderID": atcSender.uid ?? "", 120 "senderUsername": atcSender.username ?? "", 121 "senderProfilePictureURL": atcSender.profileUrl ?? "", 122 "recipientID": recipient.uid ?? "", 123 "recipientUsername": recipient.username ?? "", 124 "recipientProfilePictureURL": recipient.profileUrl ?? "", 125 ] 126 127 if let url = downloadURL { 128 rep["url"] = url.absoluteString 129 } else { 130 rep["content"] = content 131 } 132 return rep 133 } 134} 135 136extension Message: Comparable { 137 138 static func == (lhs: Message, rhs: Message) -> Bool { 139 return lhs.id == rhs.id 140 } 141 142 static func < (lhs: Message, rhs: Message) -> Bool { 143 return lhs.sentDate < rhs.sentDate 144 } 145 146}

ChatViewController

1import UIKit 2import MessageKit 3import MessageInputBar 4import Firebase 5import FirebaseFirestore 6import FirebaseAuth 7 8class ChatViewController: MessagesViewController { 9 10 private let db = Firestore.firestore() 11 private var reference: CollectionReference? 12 13 private var messages: [Message] = [] 14 private var messageListener: ListenerRegistration? 15 16 private let user: User 17 private let channel: Channel 18 19 let uid = Auth.auth().currentUser?.uid 20 21 init(user: User, channel: Channel) { 22 self.user = user 23 self.channel = channel 24 super.init(nibName: nil, bundle: nil) 25 26 title = channel.name 27 } 28 29 required init?(coder aDecoder: NSCoder) { 30 fatalError("init(coder:) has not been implemented") 31 } 32 33 deinit { 34 messageListener?.remove() 35 } 36 37 override func viewDidLoad() { 38 super.viewDidLoad() 39 40 guard let id = channel.id else { 41 navigationController?.popViewController(animated: true) 42 return 43 } 44 45 reference = db.collection(["channels", id, "thread"].joined(separator: "/")) 46 47 48 reference?.addSnapshotListener { querySnapshot, error in 49 guard let snapshot = querySnapshot else { 50 print("Error fetching snapshots: (error!)") 51 return 52 } 53 snapshot.documentChanges.forEach { diff in 54 if (diff.type == .added) { 55 print("New city: (diff.document.data())") 56 } 57 if (diff.type == .modified) { 58 print("Modified city: (diff.document.data())") 59 } 60 if (diff.type == .removed) { 61 print("Removed city: (diff.document.data())") 62 } 63 } 64 } 65 66 messageListener = reference?.addSnapshotListener { querySnapshot, error in 67 guard let snapshot = querySnapshot else { 68 print("Error listening for channel updates: (error?.localizedDescription ?? "No error")") 69 return 70 } 71 72 snapshot.documentChanges.forEach { change in 73 self.handleDocumentChange(change) 74 print("handleDocumentChange") 75 } 76 } 77 78 self.navigationItem.title = title 79 80 messageInputBar.delegate = self 81 messagesCollectionView.messagesDataSource = self 82 messagesCollectionView.messagesLayoutDelegate = self 83 messagesCollectionView.messagesDisplayDelegate = self 84 messageInputBar.sendButton.tintColor = UIColor.lightGray 85 86 //scrollsToBottomOnKeyboardBeginsEditing = true // default false 87 //maintainPositionOnKeyboardFrameChanged = true // default false 88 } 89 90 override func viewDidAppear(_ animated: Bool) { 91 super.viewDidAppear(animated) 92 93 } 94 95 96 97 98 private func save(_ message: Message) { 99 reference?.addDocument(data: message.representation) { error in 100 if let e = error { 101 print("Error sending message: (e.localizedDescription)") 102 return 103 } 104 self.messagesCollectionView.scrollToBottom() 105 } 106 } 107 108 109 private func insertNewMessage(_ message: Message) { 110 guard !messages.contains(message) else { 111 return 112 } 113 114 messages.append(message) 115 messages.sort() 116 117 let isLatestMessage = messages.index(of: message) == (messages.count - 1) 118 let shouldScrollToBottom = messagesCollectionView.isAtBottom && isLatestMessage 119 120 messagesCollectionView.reloadData() 121 122 if shouldScrollToBottom { 123 DispatchQueue.main.async { 124 self.messagesCollectionView.scrollToBottom(animated: true) 125 } 126 } 127 } 128 129 private func handleDocumentChange(_ change: DocumentChange) { 130 guard var message = Message(document: change.document) else { 131 print("return Message") 132 return 133 } 134 135 switch change.type { 136 case .added: 137 print("addmessage") 138 insertNewMessage(message) 139 default: 140 break 141 } 142 } 143 144}

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問