You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从零开始开发基于Firebase的iOS聊天应用:自动回复转人工客服

从零搭建iOS Firebase智能客服系统(自动回复→人工转接)

Hey there! I've built similar chat systems with Firebase before, so let's break this down into actionable steps—from setting up your project to implementing the auto-reply handoff logic.

1. 前期环境与Firebase配置

First things first:

  • Fire up Xcode and create a new iOS App project (I'll use Swift for examples, but Obj-C works too).
  • Head to the Firebase Console, create a new project, add your iOS app, and download the GoogleService-Info.plist. Drag this file into your Xcode project, making sure to check "Copy items if needed".
  • Use CocoaPods to install Firebase dependencies. Add these to your Podfile:
    pod 'Firebase/Firestore'
    pod 'Firebase/Auth' # For user identification
    pod 'Firebase/Messaging' # Optional, for push notifications
    
    Run pod install and open the .xcworkspace file.

2. Firestore数据结构设计

We need two core collections to manage conversations and messages—keep it simple but scalable:

  • conversations (top-level collection):
    • Each document ID maps to a user's unique ID (from Firebase Auth)
    • Fields:
      • userId: String (matches the document ID)
      • isAssignedToAgent: Bool (defaults to false, switches to true when handoff happens)
      • messageCount: Int (tracks how many messages the customer has sent)
      • createdAt: Timestamp (when the conversation started)
  • messages (sub-collection under each conversations document):
    • Each document is a single message
    • Fields:
      • senderType: String ("customer", "system", "agent")
      • content: String (the message text)
      • timestamp: Timestamp (when the message was sent)

3. Core Feature Implementation

Let's build the chat logic with a view model to keep your UI code clean.

We need to identify each customer—anonymous login is perfect for this (no sign-up required):

import FirebaseAuth

// Call this when the chat screen loads
func setupAnonymousAuth() {
    Auth.auth().signInAnonymously { result, error in
        guard let user = result?.user else {
            // Handle error, show alert to user
            return
        }
        // Store user.uid as the customer's unique ID
        UserDefaults.standard.set(user.uid, forKey: "currentUserId")
        // Initialize your chat view model here
    }
}

3.2 Chat View Model Logic

Create a ChatViewModel to handle message sending, auto-reply, and handoff:

import FirebaseFirestore

class ChatViewModel {
    private let db = Firestore.firestore()
    private var userId: String
    private var conversationRef: DocumentReference?
    
    init(userId: String) {
        self.userId = userId
        // Check if the user already has a conversation; create one if not
        let convRef = db.collection("conversations").document(userId)
        convRef.getDocument { snapshot, error in
            if let snapshot = snapshot, !snapshot.exists {
                convRef.setData([
                    "userId": userId,
                    "isAssignedToAgent": false,
                    "messageCount": 0,
                    "createdAt": Timestamp()
                ])
            }
            self.conversationRef = convRef
        }
    }
    
    // Call this when the user taps "Send"
    func sendCustomerMessage(content: String) {
        guard let convRef = conversationRef else { return }
        let messagesCollection = convRef.collection("messages")
        
        // Get current conversation state
        convRef.getDocument { snapshot, error in
            guard let data = snapshot?.data(),
                  var messageCount = data["messageCount"] as? Int,
                  let isAssigned = data["isAssignedToAgent"] as? Bool else {
                // Handle error
                return
            }
            
            messageCount += 1
            
            // First, send the customer's message
            messagesCollection.addDocument(data: [
                "senderType": "customer",
                "content": content,
                "timestamp": Timestamp()
            ])
            
            // Decide: auto-reply or handoff to agent
            if !isAssigned && messageCount <= 3 { // Let's use 3 messages as the threshold
                self.sendAutoReply(convRef: convRef)
            } else if !isAssigned {
                // Switch to agent mode
                convRef.updateData(["isAssignedToAgent": true])
                // Send a system message to notify the customer
                messagesCollection.addDocument(data: [
                    "senderType": "system",
                    "content": "Thanks for your question! We're connecting you to a support agent now.",
                    "timestamp": Timestamp()
                ])
            }
            
            // Update the message count in Firestore
            convRef.updateData(["messageCount": messageCount])
        }
    }
    
    private func sendAutoReply(convRef: DocumentReference) {
        let messagesCollection = convRef.collection("messages")
        // You can make this smarter (e.g., match keywords) but let's start with fixed replies
        let autoReplies = [
            "Hi there! How can we assist you today?",
            "Thanks for reaching out—we're here to help.",
            "One moment while we review your request..."
        ]
        
        convRef.getDocument { snapshot, error in
            guard let data = snapshot?.data(), let count = data["messageCount"] as? Int else { return }
            let reply = autoReplies[count - 1] // Match the nth customer message to the nth reply
            messagesCollection.addDocument(data: [
                "senderType": "system",
                "content": reply,
                "timestamp": Timestamp()
            ])
        }
    }
}

3.3 Real-Time Message Listening (UI Integration)

In your ChatViewController, listen for new messages to update your UI in real-time:

import UIKit
import FirebaseFirestore

class ChatViewController: UIViewController {
    @IBOutlet weak var chatTableView: UITableView!
    @IBOutlet weak var messageTextField: UITextField!
    
    private var messages = [Message]()
    private var listener: ListenerRegistration?
    private var viewModel: ChatViewModel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupChat()
    }
    
    private func setupChat() {
        guard let userId = UserDefaults.standard.string(forKey: "currentUserId") else {
            setupAnonymousAuth() // Fallback if auth wasn't done yet
            return
        }
        viewModel = ChatViewModel(userId: userId)
        startListeningToMessages()
        chatTableView.dataSource = self
        chatTableView.register(UINib(nibName: "MessageCell", bundle: nil), forCellReuseIdentifier: "MessageCell")
    }
    
    private func startListeningToMessages() {
        guard let convRef = viewModel.conversationRef else { return }
        let messagesQuery = convRef.collection("messages").order(by: "timestamp")
        
        // Listen for real-time updates
        listener = messagesQuery.addSnapshotListener { querySnapshot, error in
            guard let documents = querySnapshot?.documents else {
                // Handle error
                return
            }
            
            self.messages = documents.compactMap { doc in
                guard let senderType = doc["senderType"] as? String,
                      let content = doc["content"] as? String,
                      let timestamp = doc["timestamp"] as? Timestamp else {
                    return nil
                }
                return Message(senderType: senderType, content: content, timestamp: timestamp.dateValue())
            }
            
            self.chatTableView.reloadData()
            // Scroll to the latest message
            if !self.messages.isEmpty {
                let lastIndex = IndexPath(row: self.messages.count - 1, section: 0)
                self.chatTableView.scrollToRow(at: lastIndex, at: .bottom, animated: true)
            }
        }
    }
    
    @IBAction func sendButtonTapped(_ sender: UIButton) {
        guard let content = messageTextField.text, !content.trimmingCharacters(in: .whitespaces).isEmpty else { return }
        viewModel.sendCustomerMessage(content: content)
        messageTextField.text = ""
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Clean up listener to avoid memory leaks
        listener?.remove()
    }
}

// MARK: - UITableViewDataSource
extension ChatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messages.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageCell
        let message = messages[indexPath.row]
        cell.configure(with: message)
        return cell
    }
}

// Message model
struct Message {
    let senderType: String
    let content: String
    let timestamp: Date
}

// Custom cell example (you'll need to design this in Interface Builder)
class MessageCell: UITableViewCell {
    @IBOutlet weak var messageLabel: UILabel!
    @IBOutlet weak var messageBubble: UIView!
    
    func configure(with message: Message) {
        messageLabel.text = message.content
        // Style bubbles differently based on sender
        if message.senderType == "customer" {
            messageBubble.backgroundColor = .systemBlue
            messageLabel.textColor = .white
            // Align to right
        } else {
            messageBubble.backgroundColor = .systemGray5
            messageLabel.textColor = .black
            // Align to left
        }
    }
}

4. Agent Side Implementation (Basic)

For your support team, you can build a simple agent dashboard in the same app (or a separate one):

  • Use Firebase Auth to let agents sign in with a dedicated email/password (create these in the Firebase Console)
  • Query the conversations collection for documents where isAssignedToAgent is false and messageCount > 3 (unassigned conversations ready for handoff)
  • When an agent selects a conversation, update isAssignedToAgent to true
  • Let agents send messages with senderType: "agent"—the customer's chat view will automatically pick up these messages via the real-time listener

5. Pro Tips & Optimizations

  • Error Handling: Add proper error alerts for cases like failed message sends or auth issues—don't leave users in the dark!
  • Offline Support: Firestore automatically caches data offline, so users can send messages even without internet (they sync when back online).
  • Message Status: Add an isRead field to messages to show customers when their message has been seen.
  • Smarter Auto-Replies: Use Firebase Cloud Functions to connect to an AI API for context-aware auto-replies, instead of fixed text.
  • Push Notifications: Integrate Firebase Messaging to send notifications to customers when an agent replies, and to agents when a new conversation needs attention.

内容的提问来源于stack exchange,提问作者pooja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:46:52