19日目、メッセージング機能の続き
メッセージング機能の続きを今日も行っていきます。昨日はUIを作成したのでFirebase関連のことを今日は実装していきます。
1.Messageモデルの作成
メッセージは以下の要素を持ちます。
toIdは送る先、fromIdは差出人、profileImageUrlは送る先の人の画像、usernameも送る先の人、isFromCurrentUserは差出人は現在ログインしているユーザかと言うことを示します。
なので以下のようになります。
struct Message {
let text: String
let toId: String
let fromId: String
var timestamp: Date?
let profileImageUrl: String
let isFromCurrentUser: Bool
let username: String
var chatPartnerId: String { return isFromCurrentUser ? toId : fromId }
init(dictionary: [String: Any]) {
self.text = dictionary["text"] as? String ?? ""
self.toId = dictionary["toId"] as? String ?? ""
self.fromId = dictionary["fromId"] as? String ?? ""
self.isFromCurrentUser = fromId == Auth.auth().currentUser?.uid
self.profileImageUrl = dictionary["profileImageUrl"] as? String ?? ""
self.username = dictionary["username"] as? String ?? ""
if let timestamp = dictionary["timestamp"] as? Double {
self.timestamp = Date(timeIntervalSince1970: timestamp)
}
}
}
次にメッセージをアップロードする処理を書いていきます。
2.メッセージングのDB処理
DBの構造
メッセージのアップロード
処理の流れは以下のようになっています。
static func uploadMessage(_ message: String, to user: User, completion: ((Error?) -> Void)?) {
guard let currentUid = Auth.auth().currentUser?.uid else { return }
let data = ["text": message,
"fromId": currentUid,
"toId": user.uid,
"timestamp": Timestamp(date: Date()),
"username": user.username,
"profileImageUrl": user.profileImageUrl] as [String : Any]
COLLECTION_MESSAGES.document(currentUid).collection(user.uid).addDocument(data: data) { _ in
COLLECTION_MESSAGES.document(user.uid).collection(currentUid).addDocument(data: data, completion: completion)
COLLECTION_MESSAGES.document(currentUid).collection("recent-messages").document(user.uid).setData(data)
COLLECTION_MESSAGES.document(user.uid).collection("recent-messages").document(currentUid).setData(data)
}
}
最新のメッセージの取得
この機能はメッセージのリスト(conversationController)で呼び出される機能です。処理は以下のようになっています。
static func fetchRecentMessages(completion: @escaping([Message]) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let query = COLLECTION_MESSAGES.document(uid).collection("recent-messages").order(by: "timestamp")
query.addSnapshotListener { (snapshot, error) in
guard let documentChanges = snapshot?.documentChanges else { return }
let messages = documentChanges.map({ Message(dictionary: $0.document.data()) })
completion(messages)
}
}
メッセージの取得
この機能はトーク画面(chatController)にて呼び出される機能です。処理は以下のようになっています。
static func fetchMessages(forUser user: User, completion: @escaping([Message]) -> Void) {
guard let currentUid = Auth.auth().currentUser?.uid else { return }
var messages = [Message]()
let query = COLLECTION_MESSAGES.document(currentUid).collection(user.uid).order(by: "timestamp")
query.addSnapshotListener { (snapshot, error) in
guard let documentChanges = snapshot?.documentChanges.filter({ $0.type == .added }) else { return }
messages.append(contentsOf: documentChanges.map({ Message(dictionary: $0.document.data()) }))
completion(messages)
}
}
これをそれぞれのコントローラにて呼び出します。
3.ViewModelの作成
このアプリはMVVM構造で作成しているので次にviewにデータを渡す際のviewModelの設定をしていきます。
import UIKit
struct MessageViewModel {
private let message: Message
var messageBackgroundColor: UIColor { return message.isFromCurrentUser ? .Orange : .white }
var messageBorderWidth: CGFloat { return message.isFromCurrentUser ? 0 : 1.0 }
var rightAnchorActive: Bool { return message.isFromCurrentUser }
var leftAnchorActive: Bool { return !message.isFromCurrentUser }
var shouldHideProfileImage: Bool { return message.isFromCurrentUser }
var messageText: String { return message.text }
var username: String { return message.username }
var profileImageUrl: URL? { return URL(string: message.profileImageUrl) }
var timestampString: String? {
guard let date = message.timestamp else { return nil }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "hh:mm a"
return dateFormatter.string(from: date)
}
init(message: Message) {
self.message = message
}
}
これで一通り、メッセージ機能に必要な要素が揃いました。
いよいよ明日は、これらの要素を用いてメッセージ機能の実装を行っていきます!