[UIKit] Firebase Chat App: Loading Conversations & Messages

Junyoung Park·2022년 9월 30일
1

UIKit

목록 보기
42/142
post-thumbnail

Swift: Firebase Chat App Part 12 - Loading Conversations & Messages (Real-time) - Xcode 11 - 2020

Firebase Chat App: Loading Conversations & Messages

구현 목표

  • 특정 대상에 대한 문자: 파이어베이스 저장 및 로드
  • 특정 대상과의 문자 뷰 UI 로직 추가

구현 태스크

  1. 대화 생성 및 추가 로직 데이터베이스 매니저 클래스 사용

핵심 코드

/// Creates a new conversation with target user email and first message sent
    func createNewConversation(with otherUserEmail: String, name: String, firstMessage: Message, completionHandler: @escaping ((Bool) -> Void)) {
        guard
            let currentEmail = UserDefaults.standard.value(forKey: "email") as? String,
            let platformString = UserDefaults.standard.value(forKey: "platform") as? String,
            let platform = Platform(rawValue: platformString) else {
            completionHandler(false)
            return
        }
        let safeEmail = DatabaseManager.safeEmail(emailAddress: currentEmail, platform: platform)
        let ref = database.child("\(safeEmail)")
        ref.observeSingleEvent(of: .value) { [weak self] snapshot in
            guard let self = self else { return }
            guard var userNode = snapshot.value as? [String : Any] else {
                completionHandler(false)
                print("User not found")
                return
            }
            let messageDate = firstMessage.sentDate
            let dateString = DateFormatter.dateFormatter.string(from: messageDate)
            var message = ""
            switch firstMessage.kind {
            case .text(let messageText):
                message = messageText
                break
            case .attributedText(_):
                break
            case .photo(_):
                break
            case .video(_):
                break
            case .location(_):
                break
            case .emoji(_):
                break
            case .audio(_):
                break
            case .contact(_):
                break
            case .linkPreview(_):
                break
            case .custom(_):
                break
            @unknown default:
                break
            }
            
            let conversationID = "conversation_" + firstMessage.messageId
            
            let newConversationData: [String:Any] = [
                "id" : conversationID,
                "name" : name,
                "other_user_email" : otherUserEmail,
                "latest_message" : [
                    "date" : dateString,
                    "message" : message,
                    "is_read" : false
                ]
            ]
            
            let recipient_newConversationData: [String:Any] = [
                "id" : conversationID,
                "name" : "Self",
                "other_user_email" : safeEmail,
                "latest_message" : [
                    "date" : dateString,
                    "message" : message,
                    "is_read" : false
                ]
            ]
            // Update recipient conversation entry
            self.database.child("\(otherUserEmail)/conversations").observeSingleEvent(of: .value) { [weak self] snapshot in
                guard let self = self else { return }
                if var conversations = snapshot.value as? [[String:Any]] {
                    // append this conversation
                    conversations.append(recipient_newConversationData)
                    self.database.child("\(otherUserEmail)/conversations").setValue(conversations)
                } else {
                    // create new conversation
                    self.database.child("\(otherUserEmail)/conversations").setValue([recipient_newConversationData])
                }
            }
            
            // Update current user conversation entry
            if var conversations = userNode["conversations"] as? [[String : Any]] {
                // Conversation array exists for current user
                // you should append
                conversations.append(newConversationData)
                userNode["conversations"] = conversations
            } else {
                // Conversation array does not exist
                // Create it
                userNode["conversations"] = [
                    newConversationData
                ]
            }
            ref.setValue(userNode) { [weak self] error, _ in
                guard let self = self else { return }
                guard error == nil else {
                    completionHandler(false)
                    return
                }
                self.finishCreatingConversation(name: name, conversationID: conversationID, firstMessage: firstMessage, completionHandler: completionHandler)
            }
        }
    }
  • 다른 사람과의 대화를 생성 및 변경하는 데이터베이스 함수
/// Gets all messages for a given conversation
    func getAllMessagesForConversation(with id: String, completionHandler: @escaping ((Result<[Message], Error>) -> Void)) {
        database.child("\(id)/messages").observe(.value) { snapshot in
            guard let value = snapshot.value as? [[String:Any]] else {
                completionHandler(.failure(DatabaseErrors.failedToFetch))
                return
            }
            
            let messages: [Message] = value.compactMap { dictionary in
                guard
                    let name = dictionary["name"] as? String,
                    let type = dictionary["type"] as? String,
                    let isRead = dictionary["is_read"] as? Bool,
                    let content = dictionary["content"] as? String,
                    let messageID = dictionary["id"] as? String,
                    let senderEmail = dictionary["sender_email"] as? String,
                    let dateString = dictionary["date"] as? String,
                    let date = DateFormatter.dateFormatter.date(from: dateString) else {
                    return nil
                }
                let sender = Sender(photoURLString: "", senderId: senderEmail, displayName: name)
                
                return Message(sender: sender,
                               messageId: messageID,
                               sentDate: date,
                               kind: .text(content))
            }
            completionHandler(.success(messages))
        }
    }
  • 파이어베이스 데이터베이스 내 저장되어 있는 대화 목록을 id를 통해 검색
  • 채팅 뷰를 구성하는 데 필요한 메시지 배열을 리턴

구현 화면

profile
JUST DO IT

3개의 댓글

comment-user-thumbnail
2024년 1월 10일

Your UIKit Firebase Chat App looks promising! It's impressive to see the progress on loading conversations and messages. If you're curious about creating a full-fledged messaging app like WhatsApp, check out this insightful article for valuable insights: https://www.cleveroad.com/blog/how-much-does-it-cost-to-create-an-app-like-whatsapp/

답글 달기
comment-user-thumbnail
2025년 7월 3일

If you're building a real-time chat experience like in a Firebase chat app, having user identity verification can add a layer of trust and security. For developers targeting users in Qatar, integrating a check through the official https://visacheckqatar.com/ system could help validate user IDs before accessing sensitive messaging features. This ensures smoother onboarding and aligns with local digital ID standards.

답글 달기
comment-user-thumbnail
2025년 7월 16일

Really helpful breakdown of how to structure Firebase queries with UIKit — especially the way you're handling conversation loading! If you're planning to scale this app or demo it through video content, it might also be useful to ensure your YouTube channel is eligible for monetization. You can use this free https://monetize-checker.com/ to quickly verify your channel’s monetization status. A good way to make sure you're not missing out on revenue as you share your builds.

답글 달기