1. 總覽
歡迎參加 iOS 版 FriendlyChat 程式碼研究室!在本程式碼研究室中,您將瞭解如何使用 Firebase 和新式 Swift 功能 (例如 SwiftUI、Swift 並行和 Observation 架構),建構功能齊全的即時聊天 iOS 應用程式。
課程內容
- 使用新式觀察架構 (
@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 登入畫面。此時,登入按鈕和訊息動態饋給是預留位置。您將在下列步驟中將 Firebase 連線至應用程式。
4. 設定 Firebase 專案
建立新的 Firebase 專案
- 使用 Google 帳戶登入 Firebase 控制台。
- 按一下按鈕建立新專案,然後輸入專案名稱 (例如
FriendlyChat)。
- 按一下「繼續」。
- 如果系統提示,請詳閱並接受 Firebase 條款,然後按一下「繼續」。
- (選用) 在 Firebase 控制台中啟用 AI 輔助功能 (稱為「Gemini in Firebase」)。
- 本程式碼研究室不需要 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 - 按一下 [Register app] (註冊應用程式)。
將 GoogleService-Info.plist 新增至 Xcode 專案
- 按一下「Download GoogleService-Info.plist」儲存設定檔。
- 在 Xcode 中,將下載的
GoogleService-Info.plist檔案拖曳至「Project Navigator」中的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 主控台中啟用所選的驗證供應商,才能驗證使用者。本教學課程使用電子郵件地址/密碼驗證:
使用驗證規則保護 Cloud Firestore
接著,要求使用者先通過驗證,才能讀取或寫入訊息:
- 在 Firebase 控制台中,依序前往「資料庫和儲存空間」>「Firestore 資料庫」,然後點選「建立資料庫」。
- 選取位置,然後按一下「下一步」。
- 選取「以測試模式啟動」,然後點選「建立」。
- 選取「規則」分頁標籤,然後將安全性規則更新為:
這些規則允許任何通過驗證的使用者讀取或寫入資料庫中的訊息,適合用於學習。如要進一步瞭解安全性規則,請參閱安全性規則說明文件。rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /messages/{messageId} { allow read, write: if request.auth != null; } } } - 按一下「發布」。
在 UserViewModel.swift 中觀察驗證狀態
在 SwiftUI 中,觀察 Firebase Auth 狀態可確保使用者介面在登入畫面和即時通訊介面之間順暢切換。
開啟 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 中的「Run」按鈕。
- 在應用程式模擬器中,按一下「Sign Up」(註冊),輸入顯示名稱、電子郵件地址和密碼,然後輕觸「SIGN UP」(註冊)。
- 系統應會自動驗證您的身分,並將您導向空白訊息畫面!
6. 從 Cloud Firestore 讀取
在 Firebase 控制台中新增範例訊息
將範例訊息填入資料庫:
- 在 Firebase 控制台中,前往「Firestore Database」,然後選取「Data」分頁標籤。
- 按一下「開始收集」。
- 輸入「Collection ID」的
messages,然後按一下「Next」。 - 將「文件 ID」設為自動產生 (或按一下「自動產生的 ID」)。
- 在文件中新增下列欄位:
text(類型:string,值:Hello)displayName(類型:string,值:anonymous)
- 按一下 [儲存]。
- 如要新增更多範例訊息,請按一下「新增文件」。
同步處理「FriendlyMessageViewModel.swift」中的訊息
在 SwiftUI 中,@Observable 模型會驅動宣告式 UI 更新。開啟 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)
}
}
}
}
透過檢視畫面模型將資料庫狀態直接繫結至 UI 狀態,SwiftUI 可讓應用程式程式碼清楚地將轉場效果對應至動畫。在下一個步驟中,您將從應用程式將資料寫入資料庫,觀察這個動畫的實際運作情形。
7. 傳送訊息
使用 Swift 並行處理實作 sendMessage
使用 addDocument() 將文件新增至集合時,Cloud Firestore 會為每則即時通訊訊息產生專屬 ID。
在 ViewModels/FriendlyMessageViewModel.swift 中,將 sendMessage(text:imageUrl:) 中的 TODO 替換為 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 中的「Run」按鈕。
- 登入您的帳戶。
- 在底部的文字欄位中輸入訊息,然後輕觸「傳送」圖示。
- 訊息會立即顯示在模擬器中!
8. 儲存及接收圖片
設定 Cloud Storage for Firebase
使用 SwiftUI PhotosPicker 挑選原生圖片
新版 SwiftUI (iOS 16 以上版本) 可透過 PhotosPicker 從 import PhotosUI 原生選取相片,不需要 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 中,將 uploadAndSendImage(data:) 中的 TODO 替換為:
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以非同步方式擷取。 - 系統會使用
Storage.storage().reference(forURL:)擷取 Google Cloud Storage 網址 (例如gs://...)。
測試圖片訊息
- 按一下 Xcode 中的「Run」按鈕。
- 登入您的帳戶。
- 輕觸「相片」圖示,從圖庫中選擇圖片,然後等待上傳並顯示在對話中!
9. 結論
恭喜!您已使用 Swift 和 Firebase 成功建構 iOS 即時通訊應用程式!
目前所學內容
- Swift Package Manager (SPM):適用於
firebase-ios-sdk的 Xcode 整合。 - SwiftUI 宣告式版面配置、
PhotosPicker和ScrollViewReader自動捲動。 - Swift 並行 (
async/await),適用於 Authentication、Cloud Firestore 和 Cloud Storage。 - 電子郵件/密碼和匿名驗證,並觀察反應式狀態。
- Cloud Firestore 即時快照監聽和自動 ID 寫入。
- Cloud Storage for Firebase 二進位檔上傳和公開下載網址。