1. Tổng quan
Chào mừng bạn đến với lớp học lập trình Friendly Chat. Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng nền tảng Firebase để tạo các ứng dụng iOS. Bạn sẽ triển khai một ứng dụng trò chuyện và theo dõi hiệu suất của ứng dụng đó bằng Firebase.
Kiến thức bạn sẽ học được
- Cho phép người dùng đăng nhập.
- Đồng bộ hoá dữ liệu bằng Cơ sở dữ liệu theo thời gian thực của Firebase.
- Lưu trữ tệp nhị phân trong Bộ nhớ Firebase.
Bạn cần
- Xcode
- CocoaPods
- Một thiết bị kiểm thử chạy iOS 8.0 trở lên hoặc trình mô phỏng
Bạn sẽ sử dụng hướng dẫn này như thế nào?
Bạn đánh giá thế nào về trải nghiệm của mình khi tạo ứng dụng iOS?
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
3. Tạo ứng dụng khởi đầu
Cách tạo ứng dụng khởi đầu:
- Trong cửa sổ dòng lệnh, hãy chuyển đến thư mục
ios-starter/swift-starter
trong tệp tải mã mẫu xuống - Vòng
pod install --repo-update
- Mở tệp FriendlyChatSwift.xcworkspace để mở dự án trong Xcode.
- Nhấp vào nút
Chạy.
Sau vài giây, bạn sẽ thấy màn hình chính của Friendly Chat. Giao diện người dùng sẽ xuất hiện. Tuy nhiên, tại thời điểm này, bạn không thể đăng nhập, gửi hoặc nhận thư. Ứng dụng sẽ huỷ bỏ với một ngoại lệ cho đến khi bạn hoàn tất bước tiếp theo.
4. Thiết lập dự án Firebase
Tạo một 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òn 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 cấp phép rồi nhấp vào Tiếp tục.
Nâng cấp gói giá của 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 đám mây.
- 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 Cloud 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 Cloud 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 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 của dự án mới, hãy nhấp vào Thêm Firebase vào ứng dụng iOS.
- Nhập mã nhận dạng gói dưới dạng "
com.google.firebase.codelab.FriendlyChatSwift
". - Nhập mã nhận dạng App Store là "
123456
". - Nhấp vào Đăng ký ứng dụng.
Thêm tệp GoogleService-Info.plist vào ứng dụng
Trên màn hình thứ hai, hãy nhấp vào Tải GoogleService-Info.plist xuống để tải một tệp cấu hình chứa tất cả siêu dữ liệu cần thiết của Firebase cho ứng dụng của bạn. Sao chép tệp đó vào ứng dụng rồi thêm tệp đó vào đích FriendlyChatSwift.
Giờ đây, bạn có thể nhấp vào dấu "x" ở góc trên bên phải của cửa sổ bật lên để đóng cửa sổ này (bỏ qua bước 3 và 4) vì bạn sẽ thực hiện các bước đó tại đây.
Nhập mô-đun Firebase
Bắt đầu bằng cách đảm bảo rằng mô-đun Firebase
được nhập.
AppDelegate.swift, FCViewController.swift
import Firebase
Định cấu hình Firebase trong AppDelegate
Sử dụng phương thức "configure" trong FirebaseApp bên trong hàm application:didFinishLaunchingWithOptions để định cấu hình các dịch vụ Firebase cơ bản từ tệp .plist của bạn.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().delegate = self
return true
}
5. Xác định người dùng
Sử dụng quy tắc để hạn chế chỉ cho người dùng đã xác thực
Giờ đây, chúng ta sẽ thêm một quy tắc để yêu cầu xác thực trước khi đọc hoặc ghi bất kỳ thông báo nào. Để làm việc này, chúng ta sẽ thêm các quy tắc sau vào đối tượng dữ liệu thông báo. Trong mục Cơ sở dữ liệu của bảng điều khiển Firebase, hãy chọn Cơ sở dữ liệu theo thời gian thực, rồi nhấp vào thẻ Quy tắc. Sau đó, hãy cập nhật các quy tắc để chúng có dạng như sau:
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
Để biết thêm thông tin về cách thức hoạt động của tính năng này (bao gồm cả tài liệu về biến "auth"), hãy xem tài liệu bảo mật của Firebase.
Định cấu hình API xác thực
Trước khi ứng dụng có thể truy cập vào API Xác thực Firebase thay cho người dùng, bạn sẽ phải bật ứng dụng đó
- Chuyển đến bảng điều khiển của Firebase rồi chọn dự án của bạn
- Chọn Xác thực
- Chọn thẻ Phương thức đăng nhập
- Bật nút chuyển Google (màu xanh dương)
- Nhấn vào Lưu trên hộp thoại xuất hiện
Nếu sau này bạn gặp lỗi "CONFIGURATION_NOT_FOUND" trong lớp học lập trình này, hãy quay lại bước này và kiểm tra kỹ công việc của bạn.
Xác nhận phần phụ thuộc Firebase Auth
Xác nhận rằng các phần phụ thuộc Firebase Auth có trong tệp Podfile
.
Podfile
pod 'Firebase/Auth'
Thiết lập tệp Info.plist cho tính năng Đăng nhập bằng Google.
Bạn cần thêm một giản đồ URL tuỳ chỉnh vào dự án XCode.
- Mở cấu hình dự án: nhấp đúp vào tên dự án trong chế độ xem dạng cây ở bên trái. Chọn ứng dụng của bạn trong mục MỤC TIÊU, sau đó chọn thẻ Thông tin và mở rộng mục Loại URL.
- Nhấp vào nút + rồi thêm một giản đồ URL cho mã ứng dụng khách đảo ngược. Để tìm giá trị này, hãy mở tệp cấu hình GoogleService-Info.plist rồi tìm khoá REVERSED_CLIENT_ID. Sao chép giá trị của khoá đó rồi dán vào hộp URL Schemes (Lược đồ URL) trên trang cấu hình. Để trống các trường khác.
- Sau khi hoàn tất, cấu hình của bạn sẽ có dạng như sau (nhưng có các giá trị dành riêng cho ứng dụng của bạn):
Đặt clientID cho tính năng Đăng nhập bằng Google
Sau khi định cấu hình Firebase, chúng ta có thể sử dụng clientID để thiết lập tính năng Đăng nhập bằng Google trong phương thức "didFinishLaunchingWithOptions:".
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
Thêm trình xử lý đăng nhập
Sau khi kết quả của quy trình Đăng nhập bằng Google thành công, hãy dùng tài khoản đó để xác thực bằng Firebase.
AppDelegate.swift
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
if let error = error {
print("Error \(error)")
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print("Error \(error)")
return
}
}
}
Tự động đăng nhập người dùng. Sau đó, hãy thêm một trình nghe vào Firebase Auth để cho phép người dùng truy cập vào ứng dụng sau khi đăng nhập thành công. Và xoá trình nghe khi huỷ khởi tạo.
SignInViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signInSilently()
handle = Auth.auth().addStateDidChangeListener() { (auth, user) in
if user != nil {
MeasurementHelper.sendLoginEvent()
self.performSegue(withIdentifier: Constants.Segues.SignInToFp, sender: nil)
}
}
}
deinit {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
Đăng xuất
Thêm phương thức Đăng xuất
FCViewController.swift
@IBAction func signOut(_ sender: UIButton) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
dismiss(animated: true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError.localizedDescription)")
}
}
Kiểm thử việc đọc tin nhắn với tư cách là người dùng đã đăng nhập
- Nhấp vào nút
Chạy.
- Bạn sẽ được chuyển ngay đến màn hình đăng nhập. Nhấn vào nút Đăng nhập bằng Google.
- Sau đó, bạn sẽ được chuyển đến màn hình nhắn tin nếu mọi thứ hoạt động tốt.
6. Kích hoạt Cơ sở dữ liệu theo thời gian thực
Nhập tin nhắn
Trong dự án của bạn trên bảng điều khiển của Firebase, hãy chọn mục Cơ sở dữ liệu trên thanh điều hướng bên trái. Trong trình đơn mục bổ sung của Cơ sở dữ liệu, hãy chọn Nhập JSON. Duyệt tìm tệp initial_messages.json
trong thư mục friendlychat, chọn tệp đó rồi nhấp vào nút Import (Nhập). Thao tác này sẽ thay thế mọi dữ liệu hiện có trong cơ sở dữ liệu của bạn. Bạn cũng có thể chỉnh sửa trực tiếp cơ sở dữ liệu bằng cách dùng dấu + màu xanh lục và dấu x màu đỏ để thêm và xoá các mục.
Sau khi nhập, cơ sở dữ liệu của bạn sẽ có dạng như sau:
Xác nhận phần phụ thuộc Cơ sở dữ liệu Firebase
Trong khối phần phụ thuộc của tệp Podfile
, hãy xác nhận rằng Firebase/Database
đã được đưa vào.
Podfile
pod 'Firebase/Database'
Đồng bộ hoá tin nhắn hiện có
Thêm mã đồng bộ hoá các thông báo mới được thêm vào giao diện người dùng của ứng dụng.
Mã bạn thêm trong phần này sẽ:
- Khởi chạy cơ sở dữ liệu Firebase và thêm một trình nghe để xử lý các thay đổi được thực hiện đối với cơ sở dữ liệu.
- Cập nhật
DataSnapshot
để tin nhắn mới xuất hiện.
Sửa đổi các phương thức "deinit", "configureDatabase" và "tableView:cellForRow indexPath:" của FCViewController; thay thế bằng mã được xác định bên dưới:
FCViewController.swift
deinit {
if let refHandle = _refHandle {
self.ref.child("messages").removeObserver(withHandle: _refHandle)
}
}
func configureDatabase() {
ref = Database.database().reference()
// Listen for new messages in the Firebase database
_refHandle = self.ref.child("messages").observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
strongSelf.messages.append(snapshot)
strongSelf.clientTable.insertRows(at: [IndexPath(row: strongSelf.messages.count-1, section: 0)], with: .automatic)
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String: String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
return cell
}
Kiểm thử tính năng đồng bộ hoá tin nhắn
- Nhấp vào nút
Chạy.
- Nhấp vào nút Đăng nhập để bắt đầu để chuyển đến cửa sổ tin nhắn.
- Thêm thông báo mới ngay trong bảng điều khiển của Firebase bằng cách nhấp vào biểu tượng + màu xanh lục bên cạnh mục "messages" (thông báo) và thêm một đối tượng như sau:
- Xác nhận rằng các tin nhắn đó xuất hiện trong giao diện người dùng Friendly-Chat.
7. Gửi tin nhắn
Triển khai tính năng Gửi tin nhắn
Đẩy các giá trị vào cơ sở dữ liệu. Khi bạn sử dụng phương thức push để thêm dữ liệu vào Cơ sở dữ liệu theo thời gian thực của Firebase, một mã nhận dạng tự động sẽ được thêm vào. Các mã nhận dạng được tạo tự động này có tính tuần tự, đảm bảo rằng các thông báo mới sẽ được thêm theo đúng thứ tự.
Sửa đổi phương thức "sendMessage:" của FCViewController; thay thế bằng mã được xác định bên dưới:
FCViewController.swift
func sendMessage(withData data: [String: String]) {
var mdata = data
mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName
if let photoURL = Auth.auth().currentUser?.photoURL {
mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString
}
// Push data to Firebase Database
self.ref.child("messages").childByAutoId().setValue(mdata)
}
Kiểm thử việc gửi thông báo
- Nhấp vào nút
Chạy.
- Nhấp vào Đăng nhập để chuyển đến cửa sổ tin nhắn.
- Nhập tin nhắn rồi nhấn gửi. Thông báo mới sẽ xuất hiện trong giao diện người dùng của ứng dụng và trong bảng điều khiển của Firebase.
8. Lưu trữ và nhận hình ảnh
Xác nhận phần phụ thuộc Bộ nhớ Firebase
Trong khối phần phụ thuộc của Podfile
, hãy xác nhận rằng Firebase/Storage
đã được thêm.
Podfile
pod 'Firebase/Storage'
Thiết lập Cloud Storage cho Firebase
Sau đây là cách thiết lập Cloud Storage cho Firebase trong dự án Firebase:
- Trong bảng điều khiển bên trái của bảng điều khiển Firebase, hãy mở rộng Tạo rồi chọn Bộ nhớ.
- Nhấp vào Bắt đầu.
- Chọn một vị trí cho bộ chứa lưu trữ mặc định.
Các bộ chứa ởUS-WEST1
,US-CENTRAL1
vàUS-EAST1
có thể tận dụng cấp"Luôn miễn phí" của Google Cloud Storage. Các bộ chứa ở tất cả những vị trí khác đều tuân theo mức giá và mức sử dụng của Google Cloud Storage. - Nhấp vào Bắt đầu ở chế độ thử nghiệm. Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật.
Sau này trong lớp học lập trình này, bạn sẽ thêm các quy tắc bảo mật để bảo mật dữ liệu của mình. Không phân phối hoặc công khai ứng dụng mà không thêm Quy tắc bảo mật cho Nhóm lưu trữ. - Nhấp vào Tạo.
Định cấu hình FirebaseStorage
FCViewController.swift
func configureStorage() {
storageRef = Storage.storage().reference()
}
Nhận hình ảnh trong tin nhắn hiện có
Thêm mã tải hình ảnh xuống từ Firebase Storage.
Sửa đổi phương thức "tableView: cellForRowAt indexPath:" của FCViewController; thay thế bằng mã được xác định bên dưới:
FCViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot: DataSnapshot! = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String:String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
if let imageURL = message[Constants.MessageFields.imageURL] {
if imageURL.hasPrefix("gs://") {
Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
DispatchQueue.main.async {
cell.imageView?.image = UIImage.init(data: data!)
cell.setNeedsLayout()
}
}
} else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage.init(data: data)
}
cell.textLabel?.text = "sent by: \(name)"
} else {
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
}
return cell
}
9. Gửi tin nhắn hình ảnh
Triển khai tính năng Lưu trữ và gửi hình ảnh
Tải hình ảnh lên từ người dùng, sau đó đồng bộ hoá URL lưu trữ của hình ảnh này với cơ sở dữ liệu để hình ảnh này được gửi trong tin nhắn.
Sửa đổi phương thức "imagePickerController: didFinishPickingMediaWithInfo:" của FCViewController; thay thế bằng mã được xác định bên dưới:
FCViewController.swift
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion:nil)
guard let uid = Auth.auth().currentUser?.uid else { return }
// if it's a photo from the library, not an image from the camera
if #available(iOS 8.0, *), let referenceURL = info[UIImagePickerControllerReferenceURL] as? URL {
let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in
let imageFile = contentEditingInput?.fullSizeImageURL
let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"
guard let strongSelf = self else { return }
strongSelf.storageRef.child(filePath)
.putFile(from: imageFile!, metadata: nil) { (metadata, error) in
if let error = error {
let nsError = error as NSError
print("Error uploading: \(nsError.localizedDescription)")
return
}
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
})
} else {
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else { return }
let imageData = UIImageJPEGRepresentation(image, 0.8)
let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
self.storageRef.child(imagePath)
.putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in
if let error = error {
print("Error uploading: \(error)")
return
}
guard let strongSelf = self else { return }
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
}
}
Thử gửi và nhận tin nhắn hình ảnh
- Nhấp vào nút
Chạy.
- Nhấp vào Đăng nhập để chuyển đến cửa sổ tin nhắn.
- Nhấp vào biểu tượng "thêm ảnh" để chọn một bức ảnh. Thông báo mới kèm theo ảnh sẽ xuất hiện trong giao diện người dùng của ứng dụng và trong bảng điều khiển của Firebase.
10. Xin chúc mừng!
Bạn đã sử dụng Firebase để dễ dàng tạo một ứng dụng trò chuyện theo thời gian thực.
Nội dung đã đề cập
- Cơ sở dữ liệu theo thời gian thực
- Đăng nhập liên kết
- Bộ nhớ