Swift cho lớp học lập trình Firebase dành cho iOS

1. Tổng quan

2efe6805ef369641.png

Chào mừng bạn đến với lớp học lập trình về ứng dụng 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 ứng dụng iOS. Bạn sẽ triển khai ứng dụng nhắn tin 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.

Những gì bạn cần

  • Xcode
  • CocoaPods
  • 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?

Chỉ đọc qua Đọc và hoàn thành bài tập

Bạn đánh giá thế nào về trải nghiệm tạo ứng dụng iOS?

Tân binh Trung cấp Thành thạo

2. Tải 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 động

2f4c98d858c453fe.png

Cách tạo ứng dụng khởi động:

  1. Trong cửa sổ dòng lệnh, hãy chuyển đến thư mục android_studio_folder.pngios-starter/swift-starter từ tệp tải mã mẫu xuống
  2. Vòng pod install --repo-update
  3. Mở tệp FriendlyChatSwift.xcworkspace để mở dự án trong Xcode.
  4. Nhấp vào nút 98205811bbed9d74.pngRun (Chạy).

Sau vài giây, bạn sẽ thấy màn hình chính của ứng dụng Friendly Chat xuất hiện. 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ỷ với một ngoại lệ cho đến khi bạn hoàn tất bước tiếp theo.

4. Tạo dự án trên bảng điều khiển Firebase

Tạo dự án

Trong bảng điều khiển của Firebase, hãy chọn Thêm dự án.

Gọi dự án là FriendlyChat, sau đó nhấp vào Create Project (Tạo dự án).

Ảnh chụp màn hình từ ngày 6 tháng 11 năm 2015 14:13:39.png

Nâng cấp gói giá Firebase

Để sử dụng Cloud Storage cho Firebase, dự án Firebase của bạ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 Cloud Billing.

  • Tài khoản Cloud Billing cần 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 300 USD và tài khoản dùng thử miễn phí trên Cloud Billing hay không.
  • Nếu bạn tham gia 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ó khoản tín dụng Google 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:

  1. Trong bảng điều khiển của Firebase, hãy chọn nâng cấp gói.
  2. Chọn gói Blaze. Làm theo hướng dẫn trên màn hình để liên kết tài khoản Cloud Billing với dự án của bạn.
    Nếu cần tạo tài khoản Cloud Billing 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

  1. 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.
  2. Nhập mã gói là "com.google.firebase.codelab.FriendlyChatSwift".
  3. Nhập mã nhận dạng trong App Store là "123456".
  4. 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 tệp cấu hình chứa tất cả siêu dữ liệu Firebase cần thiết cho ứng dụng của bạn. Sao chép tệp đó vào ứng dụng và thêm tệp đó vào mục tiêu FriendlyChatSwift.

Giờ đây, bạn có thể nhấp vào biểu tượng "x" ở góc trên bên phải của cửa sổ bật lên để đóng cửa sổ đó – bỏ qua bước 3 và 4 – vì bạn sẽ thực hiện các bước đó tại đây.

19d59efb213ddbdc.png

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.

AppDelegate.swift

  func application(_ application: UIApplication, didFinishLaunchingWithOptions
      launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  FirebaseApp.configure()
  GIDSignIn.sharedInstance().delegate = self
  return true
}

5. Nhận dạng người dùng

Sử dụng quy tắc để chỉ cho phép người dùng đã xác thực truy cập

Bây giờ, 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. Để thực hiện việc này, chúng ta 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, sau đó nhấp vào thẻ Quy tắc. Sau đó, hãy cập nhật các quy tắc để có dạng như sau:

{
  "rules": {
    "messages": {
      ".read": "auth != null",
      ".write": "auth != null"
    }
  }
}

Để biết thêm thông tin về cách 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ể thay mặt người dùng truy cập vào API Xác thực Firebase, bạn phải bật API này

  1. Chuyển đến Bảng điều khiển của Firebase rồi chọn dự án của bạn
  2. Chọn Xác thực
  3. Chọn thẻ Phương thức đăng nhập
  4. Chuyển nút chuyển Google sang trạng thái bật (màu xanh dương)
  5. Nhấn Save (Lưu) trên hộp thoại xuất hiện

Nếu sau này bạn gặp lỗi trong lớp học lập trình này với thông báo "CONFIGURATION_NOT_FOUND" (KHÔNG_TÌM_THẤY_CẤU_HÌNH), 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 các phần phụ thuộc Firebase Auth có trong tệp Podfile.

Tệp pod

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 giao thức URL tuỳ chỉnh vào dự án XCode.

  1. 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 rồi mở rộng mục Loại URL.
  2. Nhấp vào nút dấu + rồi thêm giao thức 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 (Sơ đồ URL) trên trang cấu hình. Để trống các trường khác.
  3. Khi hoàn tất, cấu hình của bạn sẽ có dạng như sau (nhưng với các giá trị dành riêng cho ứng dụng):

1b54d5bd2f4f1448.png

Đặ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 bên 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ả Đăng nhập bằng Google thành công, hãy sử 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 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 deinit.

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ử tính năng Đọc thư dưới vai trò là người dùng đã đăng nhập

  1. Nhấp vào nút 98205811bbed9d74.pngRun (Chạy).
  2. 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.
  3. 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

2efe6805ef369641.png

Nhập tin nhắn

Trong dự án của bạn trong 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 đến 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 sử dụng biểu tượng dấu + màu xanh lục và dấu x màu đỏ để thêm và xoá các mục.

20ccf4856b715b4c.png

Sau khi nhập, cơ sở dữ liệu của bạn sẽ có dạng như sau:

f3e0367f1c9cd187.png

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ó trong đó.

Tệp pod

pod 'Firebase/Database'

Đồng bộ hoá thư hiện có

Thêm mã đồng bộ hoá các tin nhắn mới 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 trình nghe để xử lý các thay đổi đối với cơ sở dữ liệu.
  • Cập nhật DataSnapshot để hiển thị tin nhắn mới.

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á thư

  1. Nhấp vào nút 98205811bbed9d74.pngRun (Chạy).
  2. Nhấp vào nút Đăng nhập để bắt đầu để chuyển đến cửa sổ thư.
  3. Thêm thông báo mới ngay trong bảng điều khiển Firebase bằng cách nhấp vào biểu tượng dấu + màu xanh lục bên cạnh mục "messages" (thông báo) rồi thêm một đối tượng như sau: f9876ffc8b316b14.png
  4. Xác nhận rằng các nút này 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 đẩy để 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 theo tuần tự, đảm bảo rằng thư 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ử tính năng gửi tin nhắn

  1. Nhấp vào nút 98205811bbed9d74.pngRun (Chạy).
  2. Nhấp vào Đăng nhập để chuyển đến cửa sổ thư.
  3. Nhập tin nhắn rồi nhấn vào biểu tượng 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 đưa vào.

Tệp pod

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:

  1. Trong bảng điều khiển bên trái của Firebase, hãy mở rộng mục Bản dựng rồi chọn Bộ nhớ.
  2. Nhấp vào Bắt đầu.
  3. Chọn vị trí cho bộ chứa Bộ nhớ mặc định.
    Các bộ chứa trong US-WEST1, US-CENTRAL1US-EAST1 có thể tận dụng cấp"Luôn miễn phí" cho Google Cloud Storage. Các bộ chứa ở tất cả các vị trí khác tuân theo mức giá và mức sử dụng của Google Cloud Storage.
  4. 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.
    Trong phần sau của 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. Đừng phân phối hoặc hiển thị công khai ứng dụng mà không thêm Quy tắc bảo mật cho bộ nhớ.
  5. 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 các 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 bộ nhớ của hình ảnh này với cơ sở dữ liệu để hình ảnh này được gửi trong thư.

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])
      }
    }
  }

Kiểm thử tính năng Gửi và nhận tin nhắn hình ảnh

  1. Nhấp vào nút 98205811bbed9d74.pngRun (Chạy).
  2. Nhấp vào Đăng nhập để chuyển đến cửa sổ thư.
  3. Nhấp vào biểu tượng "thêm ảnh" để chọn ảnh. Thông báo mới có ả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 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ớ

Tìm hiểu thêm