Firebase iOS Codelab:FriendlyChat

1. 概览

欢迎学习适用于 iOS 的 FriendlyChat Codelab!在此 Codelab 中,您将学习如何使用 Firebase 和现代 Swift 功能(例如 SwiftUI、Swift 并发和 Observation 框架)构建功能齐全的实时聊天 iOS 应用。

学习内容

  • 使用现代 Observation 框架 (@Observable) 管理状态。
  • 使用 Firebase Authentication 对用户进行身份验证。
  • 使用 Cloud Firestore 实时同步聊天消息。
  • 使用 Cloud Storage for Firebase 存储和提供图片。
  • 使用现代 Swift 并发 功能访问 Firebase API。

所需条件

  • 最新版本的 Xcode
  • iOS 模拟器或实体测试设备
  • 一个用于创建和管理 Firebase 项目的 Google 账号

2. 获取示例代码

从命令行克隆 GitHub 代码库:

$ git clone https://github.com/firebase/codelab-friendlychat-ios

codelab-friendlychat-ios 代码库包含多个示例项目:

  • ios-starter/swift-starter :起始 Xcode 项目,其中包含 SwiftUI 布局框架和 TODO 注释,您将使用 Firebase 实现应用功能。
  • ios/swift :已完成的 Xcode 项目,其中包含已完成的示例应用。

3. 构建起始应用

构建并运行起始应用,以便熟悉界面:

  1. 在 Finder 或终端中,前往 ios-starter/swift-starter 目录。
  2. 双击 FriendlyChatSwift.xcodeproj 以在 Xcode 中打开项目。
  3. 请注意,Xcode 会在后台自动解析 Swift Package Manager 依赖项(从 GitHub 下载 firebase-ios-sdk v12+)。
  4. 选择一个 iOS 模拟器(例如 iPhone 17 Pro ),然后点击运行 按钮 (cmd+R)。

几秒钟后,FriendlyChat 登录界面将显示。此时,登录按钮和消息 Feed 只是占位符。您将在后续步骤中将 Firebase 连接到应用。

4. 设置 Firebase 项目

创建新的 Firebase 项目

  1. 使用您的 Google 账号登录 Firebase 控制台
  2. 点击按钮以创建新项目,然后输入项目名称(例如 FriendlyChat)。
  3. 点击继续
  4. 如果看到相关提示,请查看并接受 Firebase 条款,然后点击 继续
  5. (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
  6. 对于此 Codelab,您不需要 使用 Google Analytics(分析),因此请关闭 Google Analytics(分析)选项。
  7. 点击创建项目,等待项目完成预配,然后点击继续

升级您的 Firebase 定价方案

如需使用 Cloud Storage for Firebase,您的 Firebase 项目必须采用随用随付 (Blaze) 定价方案,这意味着该项目与一个Cloud Billing 账号相关联。

  • Cloud Billing 账号要求提供付款方式,例如信用卡。
  • 如果您刚开始接触 Firebase 和 Google Cloud,请确认您是否有资格获得 $300 赠金和免费试用 Cloud Billing 账号
  • 如果您是作为活动的一部分学习此 Codelab,请向主办方询问是否有可用的 Cloud 赠金。

如需将项目升级到 Blaze 方案,请按以下步骤操作:

  1. 在 Firebase 控制台中,选择升级您的方案
  2. 选择 Blaze 方案。按照屏幕上的说明将 Cloud Billing 账号与您的项目相关联。
    如果您需要创建 Cloud Billing 账号作为此升级的一部分,则可能需要返回 Firebase 控制台中的升级流程以完成升级。

关联您的 iOS 应用

  1. 在“项目概览”界面中,点击 iOS 图标以启动应用设置工作流。
  2. 输入软件包 ID:
    com.google.firebase.codelab.FriendlyChatSwift
    
  3. 点击注册应用

GoogleService-Info.plist 添加到您的 Xcode 项目

  1. 点击下载 GoogleService-Info.plist 以保存配置文件。
  2. 在 Xcode 中,将下载的 GoogleService-Info.plist 文件拖动到“项目导航器”中的 FriendlyChatSwift 主组。
  3. 出现提示时,确保选中Copy items if needed ,并选择 FriendlyChatSwift 目标,然后点击 Finish

FriendlyChatSwiftApp.swift 中配置 Firebase

最后,在 SwiftUI 应用启动时,使用项目配置初始化 Firebase。打开 FriendlyChatSwiftApp.swift ,并将 TODO 占位符替换为 import FirebaseCoreFirebaseApp.configure()

import SwiftUI
import FirebaseCore

@main
struct FriendlyChatSwiftApp: App {
  init() {
    FirebaseApp.configure()
  }

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

5. 对用户进行身份验证

在 Firebase 控制台中配置身份验证

您必须先在 Firebase 控制台中启用所选的身份验证提供方,然后才能对用户进行身份验证。本教程使用电子邮件地址/密码身份验证:

  1. 在 Firebase 控制台中,依次前往 Security > Authentication ,然后点击 Get started
  2. 选择 Sign-in method 标签页。
  3. 点击 Email/Password ,点击 Enable 切换开关,然后点击 Save

使用身份验证规则保护 Cloud Firestore

接下来,要求用户先通过身份验证,然后才能读取或写入消息:

  1. 在 Firebase 控制台中,依次前往 Databases & Storage > Firestore Database,然后点击 Create database
  2. 选择一个位置,然后点击 Next
  3. 选择 Start in test mode ,然后点击 Create
  4. 选择 Rules 标签页,并将安全规则更新为:
    rules_version = '2';
    
    service cloud.firestore {
      match /databases/{database}/documents {
        match /messages/{messageId} {
          allow read, write: if request.auth != null;
        }
      }
    }
    
    这些规则允许任何经过身份验证的用户在数据库中读取或写入消息,这适合学习目的。如需详细了解安全规则,请参阅安全规则文档
  5. 点击发布

UserViewModel.swift 中观察身份验证状态

在 SwiftUI 中,以响应方式观察 Firebase 身份验证状态可确保界面在登录界面和聊天界面之间平稳切换。

打开 ViewModels/UserViewModel.swift ,并将 init() 中的 TODO 替换为使用 authStateChanges AsyncSequence 观察身份验证状态变化:

  init() {
    authTask = Task {
      for await user in Auth.auth().authStateChanges {
        self.user = user
      }
    }
  }

使用 Swift 并发实现登录、注册和退出功能

UserViewModel.swift 中,将剩余的 TODO 注释替换为现代 async/await 身份验证方法:

  func signIn(email: String, password: String) async {
    errorMessage = nil
    if email.isEmpty || password.isEmpty {
      showError("Please enter both email and password.")
      return
    }
    do {
      try await Auth.auth().signIn(withEmail: email, password: password)
    } catch {
      showError(error.localizedDescription)
    }
  }

  func signUp(email: String, password: String, displayName: String) async {
    errorMessage = nil
    if email.isEmpty || password.isEmpty {
      showError("Please enter both email and password.")
      return
    }
    do {
      let result =
        try await Auth.auth().createUser(withEmail: email, password: password)
      let changeRequest = result.user.createProfileChangeRequest()
      changeRequest.displayName = displayName.isEmpty ? email : displayName
      try await changeRequest.commitChanges()
      self.user = Auth.auth().currentUser
    } catch {
      showError(error.localizedDescription)
    }
  }

  func updateDisplayName(_ displayName: String) async {
    guard let currentUser = Auth.auth().currentUser else { return }
    do {
      let changeRequest = currentUser.createProfileChangeRequest()
      changeRequest.displayName = displayName
      try await changeRequest.commitChanges()
      self.user = Auth.auth().currentUser
    } catch {
      showError(error.localizedDescription)
    }
  }

  func signOut() {
    do {
      try Auth.auth().signOut()
    } catch {
      showError(error.localizedDescription)
    }
  }

测试用户身份验证

  1. 点击 Xcode 中的运行 按钮。
  2. 在应用模拟器中,点击 Sign Up ,输入显示名称、电子邮件地址和密码,然后点按 SIGN UP
  3. 系统应该会自动对您进行身份验证,并导航到空白消息界面!

6. 从 Cloud Firestore 读取内容

在 Firebase 控制台中添加示例消息

使用示例消息填充数据库:

  1. 在 Firebase 控制台中,前往 Firestore Database ,然后选择 Data 标签页。
  2. 点击启动集合
  3. Collection ID 中输入 messages,然后点击 Next
  4. Document ID 设置为自动生成(或点击 Auto-ID )。
  5. 向文档添加以下字段:
    • text(类型:string,值:Hello
    • displayName(类型:string,值:anonymous
  6. 点击保存
  7. (可选)点击添加文档 以添加更多示例消息。

FriendlyMessageViewModel.swift 中同步消息

在 SwiftUI 中,@Observable 模型驱动声明式界面更新。打开 ViewModels/FriendlyMessageViewModel.swift ,并使用 snapshots AsyncSequence 实现 startListening()stopListening()

  func startListening() {
    stopListening()
    listenerTask = Task {
      let db = Firestore.firestore()
      do {
        for try await snapshot in db.collection("messages").snapshots {
          self.messages = snapshot.documents.compactMap { document in
            try? document.data(as: FriendlyMessage.self)
          }
        }
      } catch {
        print("Error listening for messages: \(error)")
      }
    }
  }

  func stopListening() {
    listenerTask?.cancel()
    listenerTask = nil
    messages.removeAll()
  }

SwiftUI 如何呈现消息和自动滚动消息

Views/ContentView.swift 中,请注意 ScrollViewReaderLazyVStack 如何呈现聊天气泡,以及每当 messages.count 发生变化时如何自动滚动到最新消息:

ScrollViewReader { scrollViewReader in
  ScrollView {
    LazyVStack(spacing: 12) {
      ForEach(messageViewModel.messages) { message in
        FriendlyMessageView(friendlyMessage: message)
          .id(message.id)
      }
    }
    .padding(.horizontal)
    .onChange(of: messageViewModel.messages.count) { _, count in
      guard count > 0,
            let lastId = messageViewModel.messages.last?.id else { return }
      withAnimation(.easeInOut) {
        scrollViewReader.scrollTo(lastId, anchor: .bottom)
      }
    }
  }
}

通过视图模型将数据库状态直接与界面状态相关联,SwiftUI 允许应用代码将转换干净地映射到动画。在下一步中,您将从应用向数据库写入数据,以观察此动画的实际效果。

7. 发送消息

使用 Swift 并发实现 sendMessage

使用 addDocument() 向集合添加文档时,Cloud Firestore 会为每条聊天消息生成一个唯一 ID。

ViewModels/FriendlyMessageViewModel.swift 中,将 TODO 中的 sendMessage(text:imageUrl:) 替换为 async throws 方法:

  func sendMessage(text: String?, imageUrl: String?) async throws {
    guard let currentUser = Auth.auth().currentUser else { return }
    let message = FriendlyMessage(
      text: text,
      displayName: currentUser.displayName ?? currentUser.email ?? "Anonymous",
      imageUrl: imageUrl,
      userId: currentUser.uid
    )
    let db = Firestore.firestore()
    _ = try db.collection("messages").addDocument(from: message)
  }

测试发送消息

  1. 点击 Xcode 中的运行 按钮。
  2. 登录到您的账户。
  3. 在底部文本字段中输入消息,然后点按“发送”图标。
  4. 观看消息在模拟器中即时显示!

8. 存储和接收图片

设置 Cloud Storage for Firebase

  1. 在 Firebase 控制台中,依次前往 Databases & Storage > Storage ,然后点击 Get started
  2. 选择 Start in test mode ,然后点击 Next
  3. 接受默认存储位置,然后点击 Done

使用 SwiftUI PhotosPicker 进行原生图片选择

现代 SwiftUI (iOS 16+) 通过 import PhotosUI 中的 PhotosPicker 提供原生照片选择功能,无需 UIKit 委托桥接或 UIImagePickerController 封装容器!

Views/FooterView.swift 中,观察 PhotosPicker 如何绑定所选图片:

PhotosPicker(selection: $selectedItem, matching: .images) {
  Image(systemName: "photo.on.rectangle.angled")
    .font(.system(size: 26))
    .foregroundStyle(.blue)
    .accessibilityLabel("Select photo")
}
.onChange(of: selectedItem) { _, newItem in
  Task {
    if let data = try? await newItem?.loadTransferable(type: Data.self) {
      await uploadAndSendImage(data: data)
    }
  }
}

实现图片上传

选择照片后,将图片数据上传到 Cloud Storage,并将公开下载网址保存在 Cloud Firestore 中。

Views/FooterView.swift 中,替换 TODO 中的 uploadAndSendImage(data:)

  private func uploadAndSendImage(data: Data) async {
    guard let uid = Auth.auth().currentUser?.uid else { return }
    isUploading = true
    defer {
      isUploading = false
      selectedItem = nil
    }
    do {
      let filename = "\(uid)/\(UUID().uuidString).jpg"
      let storageRef =
        Storage.storage().reference().child("images").child(filename)
      let metadata = StorageMetadata()
      metadata.contentType = "image/jpeg"

      _ = try await storageRef.putDataAsync(data, metadata: metadata)
      let downloadURL = try await storageRef.downloadURL()
      try await viewModel.sendMessage(
        text: nil, imageUrl: downloadURL.absoluteString)
    } catch {
      print("Error uploading image: \(error.localizedDescription)")
    }
  }

使用 Cloud Storage 共享图片

Views/FriendlyMessageImageView.swift 中,系统使用 Swift 并发和 Cloud Storage SDK 异步加载图片网址:

  • 标准 HTTP/HTTPS 下载网址通过 URLSession 异步检索。
  • Google Cloud Storage 网址(例如 gs://...)使用 Storage.storage().reference(forURL:) 提取。

测试图片消息

  1. 点击 Xcode 中的运行 按钮。
  2. 登录到您的账户。
  3. 点按“照片”图标,从图库中选择一张图片,然后观看图片上传并显示在聊天中!

9. 总结

恭喜,您已成功使用 Swift 和 Firebase 构建了一个实时 iOS 聊天应用!

您学到的内容

  • Swift Package Manager (SPM) Xcode 集成,用于 firebase-ios-sdk
  • SwiftUI 声明式布局、PhotosPickerScrollViewReader 自动滚动。
  • Swift 并发 (async/await) ,涵盖身份验证、Cloud Firestore 和 Cloud Storage。
  • 电子邮件地址/密码和匿名身份验证 ,具有响应式状态观察功能。
  • Cloud Firestore 实时快照监听和自动 ID 写入。
  • Cloud Storage for Firebase 二进制文件上传和公开下载网址。

了解详情