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. 构建起始应用
构建并运行起始应用,以便熟悉界面:
- 在 Finder 或终端中,前往
ios-starter/swift-starter目录。 - 双击
FriendlyChatSwift.xcodeproj以在 Xcode 中打开项目。 - 请注意,Xcode 会在后台自动解析 Swift Package Manager 依赖项(从 GitHub 下载
firebase-ios-sdkv12+)。 - 选择一个 iOS 模拟器(例如 iPhone 17 Pro ),然后点击运行 按钮 (cmd+R)。
几秒钟后,FriendlyChat 登录界面将显示。此时,登录按钮和消息 Feed 只是占位符。您将在后续步骤中将 Firebase 连接到应用。
4. 设置 Firebase 项目
创建新的 Firebase 项目
- 使用您的 Google 账号登录 Firebase 控制台。
- 点击按钮以创建新项目,然后输入项目名称(例如
FriendlyChat)。
- 点击继续 。
- 如果看到相关提示,请查看并接受 Firebase 条款,然后点击 继续。
- (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
- 对于此 Codelab,您不需要 使用 Google Analytics(分析),因此请关闭 Google Analytics(分析)选项。
- 点击创建项目,等待项目完成预配,然后点击继续。
升级您的 Firebase 定价方案
如需使用 Cloud Storage for Firebase,您的 Firebase 项目必须采用随用随付 (Blaze) 定价方案,这意味着该项目与一个Cloud Billing 账号相关联。
- Cloud Billing 账号要求提供付款方式,例如信用卡。
- 如果您刚开始接触 Firebase 和 Google Cloud,请确认您是否有资格获得 $300 赠金和免费试用 Cloud Billing 账号。
- 如果您是作为活动的一部分学习此 Codelab,请向主办方询问是否有可用的 Cloud 赠金。
如需将项目升级到 Blaze 方案,请按以下步骤操作:
- 在 Firebase 控制台中,选择升级您的方案。
- 选择 Blaze 方案。按照屏幕上的说明将 Cloud Billing 账号与您的项目相关联。
如果您需要创建 Cloud Billing 账号作为此升级的一部分,则可能需要返回 Firebase 控制台中的升级流程以完成升级。
关联您的 iOS 应用
- 在“项目概览”界面中,点击 iOS 图标以启动应用设置工作流。
- 输入软件包 ID:
com.google.firebase.codelab.FriendlyChatSwift - 点击注册应用 。
将 GoogleService-Info.plist 添加到您的 Xcode 项目
- 点击下载 GoogleService-Info.plist 以保存配置文件。
- 在 Xcode 中,将下载的
GoogleService-Info.plist文件拖动到“项目导航器”中的FriendlyChatSwift主组。 - 出现提示时,确保选中Copy items if needed ,并选择
FriendlyChatSwift目标,然后点击 Finish 。
在 FriendlyChatSwiftApp.swift 中配置 Firebase
最后,在 SwiftUI 应用启动时,使用项目配置初始化 Firebase。打开 FriendlyChatSwiftApp.swift ,并将 TODO 占位符替换为 import FirebaseCore 和 FirebaseApp.configure():
import SwiftUI
import FirebaseCore
@main
struct FriendlyChatSwiftApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
5. 对用户进行身份验证
在 Firebase 控制台中配置身份验证
您必须先在 Firebase 控制台中启用所选的身份验证提供方,然后才能对用户进行身份验证。本教程使用电子邮件地址/密码身份验证:
- 在 Firebase 控制台中,依次前往 Security > Authentication ,然后点击 Get started 。
- 选择 Sign-in method 标签页。
- 点击 Email/Password ,点击 Enable 切换开关,然后点击 Save 。
使用身份验证规则保护 Cloud Firestore
接下来,要求用户先通过身份验证,然后才能读取或写入消息:
- 在 Firebase 控制台中,依次前往 Databases & Storage > Firestore Database,然后点击 Create database。
- 选择一个位置,然后点击 Next 。
- 选择 Start in test mode ,然后点击 Create 。
- 选择 Rules 标签页,并将安全规则更新为:
这些规则允许任何经过身份验证的用户在数据库中读取或写入消息,这适合学习目的。如需详细了解安全规则,请参阅安全规则文档。rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /messages/{messageId} { allow read, write: if request.auth != null; } } } - 点击发布 。
在 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)
}
}
测试用户身份验证
- 点击 Xcode 中的运行 按钮。
- 在应用模拟器中,点击 Sign Up ,输入显示名称、电子邮件地址和密码,然后点按 SIGN UP 。
- 系统应该会自动对您进行身份验证,并导航到空白消息界面!
6. 从 Cloud Firestore 读取内容
在 Firebase 控制台中添加示例消息
使用示例消息填充数据库:
- 在 Firebase 控制台中,前往 Firestore Database ,然后选择 Data 标签页。
- 点击启动集合 。
- 在 Collection ID 中输入
messages,然后点击 Next。 - 将 Document ID 设置为自动生成(或点击 Auto-ID )。
- 向文档添加以下字段:
text(类型:string,值:Hello)displayName(类型:string,值:anonymous)
- 点击保存 。
- (可选)点击添加文档 以添加更多示例消息。
在 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 中,请注意 ScrollViewReader 和 LazyVStack 如何呈现聊天气泡,以及每当 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)
}
测试发送消息
- 点击 Xcode 中的运行 按钮。
- 登录到您的账户。
- 在底部文本字段中输入消息,然后点按“发送”图标。
- 观看消息在模拟器中即时显示!
8. 存储和接收图片
设置 Cloud Storage for Firebase
- 在 Firebase 控制台中,依次前往 Databases & Storage > Storage ,然后点击 Get started 。
- 选择 Start in test mode ,然后点击 Next 。
- 接受默认存储位置,然后点击 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:)提取。
测试图片消息
- 点击 Xcode 中的运行 按钮。
- 登录到您的账户。
- 点按“照片”图标,从图库中选择一张图片,然后观看图片上传并显示在聊天中!
9. 总结
恭喜,您已成功使用 Swift 和 Firebase 构建了一个实时 iOS 聊天应用!
您学到的内容
- Swift Package Manager (SPM) Xcode 集成,用于
firebase-ios-sdk。 - SwiftUI 声明式布局、
PhotosPicker和ScrollViewReader自动滚动。 - Swift 并发 (
async/await) ,涵盖身份验证、Cloud Firestore 和 Cloud Storage。 - 电子邮件地址/密码和匿名身份验证 ,具有响应式状态观察功能。
- Cloud Firestore 实时快照监听和自动 ID 写入。
- Cloud Storage for Firebase 二进制文件上传和公开下载网址。