1. Tổng quan
Chào mừng bạn đến với lớp học lập trình FriendlyChat dành cho iOS! Trong lớp học lập trình này, bạn sẽ tìm hiểu cách tạo một ứng dụng trò chuyện theo thời gian thực đầy đủ tính năng cho iOS bằng Firebase và các tính năng hiện đại của Swift như SwiftUI, Swift Concurrency và khung Observation.
Kiến thức bạn sẽ học được
- Quản lý trạng thái bằng khung Quan sát hiện đại (
@Observable). - Xác thực người dùng bằng Xác thực Firebase.
- Đồng bộ hoá tin nhắn trò chuyện theo thời gian thực bằng Cloud Firestore.
- Lưu trữ và phân phát hình ảnh bằng Cloud Storage cho Firebase.
- Truy cập vào các API của Firebase bằng các tính năng hiện đại của Swift Concurrency.
Bạn cần có
- Phiên bản mới nhất của Xcode
- Trình mô phỏng iOS hoặc thiết bị kiểm thử thực
- Một Tài khoản Google để tạo và quản lý dự án Firebase
2. Nhận mã mẫu
Sao chép kho lưu trữ GitHub từ dòng lệnh:
$ git clone https://github.com/firebase/codelab-friendlychat-ios
Kho lưu trữ codelab-friendlychat-ios chứa một số dự án mẫu:
ios-starter/swift-starter– Dự án Xcode ban đầu chứa bộ khung bố cục SwiftUI và các nhận xétTODOnơi bạn sẽ triển khai các tính năng của ứng dụng bằng Firebase.ios/swift– Dự án Xcode đã hoàn tất với ứng dụng mẫu hoàn chỉnh.
3. Tạo ứng dụng ban đầu
Tạo và chạy ứng dụng khởi động để làm quen với giao diện người dùng:
- Trong Finder hoặc Terminal, hãy chuyển đến thư mục
ios-starter/swift-starter. - Nhấp đúp vào
FriendlyChatSwift.xcodeprojđể mở dự án trong Xcode. - Xin lưu ý rằng Xcode tự động phân giải các phần phụ thuộc của Swift Package Manager ở chế độ nền (tải
firebase-ios-sdkphiên bản 12 trở lên xuống từ GitHub). - Chọn một Trình mô phỏng iOS (ví dụ: iPhone 17 Pro) rồi nhấp vào nút Run (Chạy) (cmd+R).
Sau vài giây, màn hình đăng nhập FriendlyChat sẽ xuất hiện. Tại thời điểm này, các nút đăng nhập và nguồn cấp dữ liệu tin nhắn chỉ là phần giữ chỗ. Bạn sẽ kết nối Firebase với ứng dụng theo các bước sau.
4. Thiết lập dự án Firebase
Tạo dự án Firebase mới
- Đăng nhập vào bảng điều khiển của Firebase bằng Tài khoản Google của bạn.
- Nhấp vào nút này để tạo một dự án mới, rồi nhập tên dự án (ví dụ:
FriendlyChat).
- Nhấp vào Tiếp tục.
- Nếu được nhắc, hãy xem xét và chấp nhận các điều khoản của Firebase, rồi nhấp vào Tiếp tục.
- (Không bắt buộc) Bật tính năng hỗ trợ của AI trong bảng điều khiển của Firebase (được gọi là "Gemini trong Firebase").
- Đối với lớp học lập trình này, bạn không cần Google Analytics, vì vậy hãy tắt lựa chọn Google Analytics.
- Nhấp vào Tạo dự án, đợi dự án của bạn được cung cấp, rồi nhấp vào Tiếp tục.
Nâng cấp gói giá Firebase
Để sử dụng Cloud Storage cho Firebase, dự án Firebase của bạn cần phải sử dụng gói giá trả theo mức sử dụng (Blaze), tức là dự án đó được liên kết với một tài khoản thanh toán trên Cloud.
- Tài khoản thanh toán trên Cloud yêu cầu bạn phải có một phương thức thanh toán, chẳng hạn như thẻ tín dụng.
- Nếu bạn mới sử dụng Firebase và Google Cloud, hãy kiểm tra xem bạn có đủ điều kiện nhận khoản tín dụng trị giá 300 USD và tài khoản thanh toán trên đám mây dùng thử miễn phí hay không.
- Nếu bạn đang thực hiện lớp học lập trình này trong một sự kiện, hãy hỏi người tổ chức xem có Tín dụng Cloud nào không.
Để nâng cấp dự án lên gói Blaze, hãy làm theo các bước sau:
- Trong bảng điều khiển của Firebase, hãy chọn nâng cấp gói.
- Chọn gói Blaze. Làm theo hướng dẫn trên màn hình để liên kết một tài khoản thanh toán trên đám mây với dự án của bạn.
Nếu cần tạo một tài khoản thanh toán trên đám mây trong quá trình nâng cấp này, bạn có thể cần quay lại quy trình nâng cấp trong bảng điều khiển của Firebase để hoàn tất quá trình nâng cấp.
Kết nối ứng dụng iOS
- Trên màn hình Tổng quan về dự án, hãy nhấp vào biểu tượng iOS để chạy quy trình thiết lập ứng dụng.
- Nhập mã nhận dạng gói:
com.google.firebase.codelab.FriendlyChatSwift - Nhấp vào Đăng ký ứng dụng.
Thêm GoogleService-Info.plist vào dự án Xcode
- Nhấp vào Tải GoogleService-Info.plist xuống để lưu tệp cấu hình.
- Trong Xcode, hãy kéo tệp
GoogleService-Info.plistđã tải xuống vào nhóm chínhFriendlyChatSwifttrong Project Navigator (Trình điều hướng dự án). - Khi được nhắc, hãy đảm bảo bạn đã chọn Sao chép các mục nếu cần và chọn đích đến là
FriendlyChatSwift, sau đó nhấp vào Hoàn tất.
Định cấu hình Firebase trong FriendlyChatSwiftApp.swift
Cuối cùng, hãy khởi động Firebase bằng cấu hình dự án khi ứng dụng SwiftUI của bạn khởi chạy. Mở FriendlyChatSwiftApp.swift rồi thay thế trình giữ chỗ TODO bằng import FirebaseCore và FirebaseApp.configure():
import SwiftUI
import FirebaseCore
@main
struct FriendlyChatSwiftApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
5. Xác thực người dùng
Định cấu hình quy trình xác thực trong bảng điều khiển của Firebase
Trước khi có thể xác thực người dùng, trước tiên, bạn phải bật nhà cung cấp dịch vụ xác thực mà bạn chọn trong bảng điều khiển của Firebase. Hướng dẫn này sử dụng phương thức xác thực Email/Mật khẩu:
- Trong bảng điều khiển của Firebase, hãy chuyển đến phần Bảo mật > Xác thực, rồi nhấp vào Bắt đầu.
- Chọn thẻ Phương thức đăng nhập.
- Nhấp vào Email/Mật khẩu, nhấp vào nút bật/tắt Bật rồi nhấp vào Lưu.
Bảo mật Cloud Firestore bằng các quy tắc xác thực
Tiếp theo, yêu cầu người dùng xác thực trước khi đọc hoặc ghi tin nhắn:
- Trong bảng điều khiển của Firebase, hãy chuyển đến Cơ sở dữ liệu và bộ nhớ > Cơ sở dữ liệu Firestore, rồi nhấp vào Tạo cơ sở dữ liệu.
- Chọn một vị trí rồi nhấp vào Tiếp theo.
- Chọn Bắt đầu ở chế độ thử nghiệm rồi nhấp vào Tạo.
- Chọn thẻ Quy tắc rồi cập nhật các quy tắc bảo mật thành:
Các quy tắc này cho phép mọi người dùng đã xác thực đọc hoặc ghi tin nhắn vào cơ sở dữ liệu, phù hợp cho mục đích học tập. Tìm hiểu thêm về các quy tắc bảo mật trong tài liệu về quy tắc bảo mật.rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /messages/{messageId} { allow read, write: if request.auth != null; } } } - Nhấp vào Xuất bản.
Theo dõi trạng thái xác thực trong UserViewModel.swift
Trong SwiftUI, việc quan sát trạng thái Xác thực Firebase một cách phản ứng giúp giao diện người dùng của bạn chuyển đổi mượt mà giữa màn hình đăng nhập và giao diện trò chuyện.
Mở ViewModels/UserViewModel.swift và thay thế TODO trong init() để quan sát các thay đổi về trạng thái uỷ quyền bằng cách sử dụng authStateChanges AsyncSequence:
init() {
authTask = Task {
for await user in Auth.auth().authStateChanges {
self.user = user
}
}
}
Triển khai tính năng đăng nhập, đăng ký và đăng xuất bằng Swift Concurrency
Trong UserViewModel.swift, hãy thay thế các nhận xét TODO còn lại bằng các phương thức xác thực async/await hiện đại:
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)
}
}
Kiểm thử quy trình xác thực người dùng
- Nhấp vào nút Run (Chạy) trong Xcode.
- Trong trình mô phỏng ứng dụng, hãy nhấp vào Đăng ký, nhập Tên hiển thị, Email và Mật khẩu, rồi nhấn vào ĐĂNG KÝ.
- Bạn sẽ được tự động xác thực và chuyển đến màn hình nhắn tin trống!
6. Đọc từ Cloud Firestore
Thêm tin nhắn mẫu trong bảng điều khiển của Firebase
Điền dữ liệu vào cơ sở dữ liệu bằng các thông báo mẫu:
- Trong bảng điều khiển của Firebase, hãy chuyển đến Cơ sở dữ liệu Firestore rồi chọn thẻ Dữ liệu.
- Nhấp vào Bắt đầu thu thập.
- Nhập
messagescho Mã nhận dạng bộ sưu tập, rồi nhấp vào Tiếp theo. - Để Mã nhận dạng tài liệu ở chế độ tự động tạo (hoặc nhấp vào Tự động tạo mã nhận dạng).
- Thêm các trường sau vào tài liệu:
text(loại:string, giá trị:Hello)displayName(loại:string, giá trị:anonymous)
- Nhấp vào Lưu.
- Bạn có thể nhấp vào Thêm tài liệu để thêm nhiều tin nhắn mẫu hơn.
Đồng bộ hoá thư trong FriendlyMessageViewModel.swift
Trong SwiftUI, mô hình @Observable điều khiển các bản cập nhật giao diện người dùng khai báo. Mở ViewModels/FriendlyMessageViewModel.swift rồi triển khai startListening() và stopListening() bằng snapshots AsyncSequence:
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()
}
Cách SwiftUI kết xuất và tự động cuộn tin nhắn
Trong Views/ContentView.swift, hãy lưu ý cách ScrollViewReader và LazyVStack hiển thị bong bóng trò chuyện và tự động cuộn đến tin nhắn mới nhất bất cứ khi nào messages.count thay đổi:
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)
}
}
}
}
Bằng cách liên kết trạng thái cơ sở dữ liệu trực tiếp với trạng thái giao diện người dùng thông qua mô hình thành phần hiển thị, SwiftUI cho phép mã ứng dụng ánh xạ các hiệu ứng chuyển đổi một cách rõ ràng sang các ảnh động. Trong bước tiếp theo, bạn sẽ ghi dữ liệu vào cơ sở dữ liệu từ ứng dụng để quan sát hiệu ứng động này trong thực tế.
7. Gửi tin nhắn
Triển khai sendMessage bằng Swift Concurrency
Khi bạn thêm một tài liệu vào một bộ sưu tập bằng cách sử dụng addDocument(), Cloud Firestore sẽ tạo một mã nhận dạng duy nhất cho mỗi tin nhắn trò chuyện.
Trong ViewModels/FriendlyMessageViewModel.swift, hãy thay thế TODO trong sendMessage(text:imageUrl:) bằng phương thức 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)
}
Thử gửi tin nhắn
- Nhấp vào nút Run (Chạy) trong Xcode.
- Đăng nhập vào tài khoản của bạn.
- Nhập tin nhắn vào trường văn bản ở dưới cùng rồi nhấn vào biểu tượng Gửi.
- Xem thông báo xuất hiện ngay lập tức trong trình mô phỏng!
8. Lưu trữ và nhận hình ảnh
Thiết lập Cloud Storage cho Firebase
- Trong bảng điều khiển của Firebase, hãy chuyển đến Cơ sở dữ liệu và bộ nhớ > Bộ nhớ, rồi nhấp vào Bắt đầu.
- Chọn Bắt đầu ở chế độ kiểm thử rồi nhấp vào Tiếp theo.
- Chấp nhận vị trí lưu trữ mặc định rồi nhấp vào Xong.
Chọn hình ảnh gốc bằng SwiftUI PhotosPicker
SwiftUI hiện đại (iOS 16 trở lên) cung cấp tính năng chọn ảnh gốc thông qua PhotosPicker từ import PhotosUI – không cần cầu nối uikit delegate hoặc trình bao bọc UIImagePickerController!
Trong Views/FooterView.swift, hãy quan sát cách PhotosPicker liên kết các hình ảnh đã chọn:
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)
}
}
}
Triển khai tính năng tải hình ảnh lên
Khi một bức ảnh được chọn, hãy tải dữ liệu hình ảnh lên Cloud Storage và lưu URL tải công khai trong Cloud Firestore.
Trong Views/FooterView.swift, hãy thay thế TODO trong 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)")
}
}
Chia sẻ hình ảnh bằng Cloud Storage
Trong Views/FriendlyMessageImageView.swift, URL hình ảnh được tải không đồng bộ bằng cách sử dụng Swift Concurrency và Cloud Storage SDK:
- URL tải xuống HTTP/HTTPS tiêu chuẩn được truy xuất không đồng bộ thông qua
URLSession. - URL Google Cloud Storage (ví dụ:
gs://...) được tìm nạp bằngStorage.storage().reference(forURL:).
Kiểm thử tin nhắn hình ảnh
- Nhấp vào nút Run (Chạy) trong Xcode.
- Đăng nhập vào tài khoản của bạn.
- Nhấn vào biểu tượng Ảnh, chọn một hình ảnh trong thư viện rồi xem hình ảnh đó tải lên và xuất hiện trong cuộc trò chuyện!
9. Kết luận
Xin chúc mừng! Bạn đã tạo thành công một ứng dụng trò chuyện theo thời gian thực trên iOS bằng Swift và Firebase!
Kiến thức bạn học được
- Trình quản lý gói Swift (SPM) Tích hợp Xcode cho
firebase-ios-sdk. - Bố cục khai báo SwiftUI,
PhotosPickervàScrollViewReadertự động cuộn. - Tính năng Xử lý đồng thời trong Swift (
async/await) trên các dịch vụ Xác thực, Cloud Firestore và Cloud Storage. - Xác thực bằng email/mật khẩu và xác thực ẩn danh bằng tính năng theo dõi trạng thái phản ứng.
- Tính năng nghe ảnh chụp nhanh theo thời gian thực và ghi mã nhận dạng tự động của Cloud Firestore.
- Tải tệp nhị phân lên và URL tải công khai xuống của Cloud Storage cho Firebase.