Firebase là một dịch vụ của google hỗ trợ rât tốt cho các nền tảng Android, Ios, Web như là hỗ trợ push notification, chat realtime... Trong thời gian gần đây dự án mà tôi tham gia cũng đã sử dụng cả push notification và chat realtime. Trong khuôn khổ blog này tôi xin giới thiệu các bước cơ bản để viết ứng dụng realtime trên IOS.

I . Chuẩn bị cơ bản

Trước tiên hãy tải project chứa màn hình login dummy theo link sau simple project here
Trong podfile hãy add nhưng thư viện sau vào

 pod 'Firebase/Storage'
 pod 'Firebase/Auth'
 pod 'Firebase/Database'
 pod 'JSQMessagesViewController'

Thực hiện lệnh sau để cài đặt các thư viện đã add ở trên

pod install

Tạo Firebase Account

Tạo account Firebase sau đó tạo một project mới trên Firebase, bước tiếp theo là tạo một app cho IOS, cuối cùng hãy download file GoogleService-Info.plist và add vào Project.
Bây giờ build app, màn hình nhìn thấy sẽ như sau

Enabling Anonymous Authentication

Để set up anonymous authentication, mở Firebase App Dashboard, chọn Auth, click vào Sign-In Method, chọn Anonymous, chuyển sang Enable và click Save

II . Màn hình đăng nhập

Mở file LoginViewController.swift, sau đó add đoạn code sau vào gần import UIKit:

import Firebase

Để log in cho chat, ứng dụng cần authenticate dùng Firebase authentication service. Thêm đoạn code sau vào trong hàm loginDidTouch(_:):

if nameField?.text != "" { // 1
  FIRAuth.auth()?.signInAnonymously(completion: { (user, error) in // 2
    if let err = error { // 3
      print(err.localizedDescription)
      return
    }
    self.performSegue(withIdentifier: "LoginToChat", sender: nil) // 4
  })
}

Giải thích chút về đoạn code trên
Thứ nhất, kiểm tra xem tên có empty không
Sau đó dùng Firebase Auth API để sign in anonymously. Nếu không hoàn thành sẽ xảy ra lỗi
Cuối cùng, nếu không bị lỗi, sẽ move tới ChannelListViewController.
Build và run app, nhập tên của bạn và và press Login anonymously

III . Tạo danh sách Channel

Các bước thực hiện sẽ như sau

1. Lưu dữ liệu vào Firebase database.
2. Để bắt đầu, thêm đoạn code sau vào đầu file ChannelListViewController.swift:
import Firebase
enum Section: Int {
  case createNewChannelSection = 0
  case currentChannelsSection  
}

Tiếp theo, trong class, thêm đoạn sau vào:

// MARK: Properties
var senderDisplayName: String? // 1
var newChannelTextField: UITextField? // 2
private var channels: [Channel] = [] // 3 

Giải thích đoạn code trên:

  1. Thêm thuộc tính để lưu tên của người gửi.
  2. Thêm text field, cái này sẽ dùng cho việc thêm một channel mới.
  3. Tạo một mảng rỗng của Channel Objects để lưu kênh của bạn.
    Tiếp theo, cần set up UITableView để render channel mới. Thêm hàm sau vào class ChannelListViewController.swift
// MARK: UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
  return 2 // 1
}  
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 2
  if let currentSection: Section = Section(rawValue: section) {
    switch currentSection {
    case .createNewChannelSection:
      return 1
    case .currentChannelsSection:
      return channels.count
    }
  } else {
    return 0
  }
}
// 3
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let reuseIdentifier = (indexPath as NSIndexPath).section == Section.createNewChannelSection.rawValue ? "NewChannel" : "ExistingChannel"
  let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
  if (indexPath as NSIndexPath).section == Section.createNewChannelSection.rawValue {
    if let createNewChannelCell = cell as? CreateChannelCell {
      newChannelTextField = createNewChannelCell.newChannelNameField
    }
  } else if (indexPath as NSIndexPath).section == Section.currentChannelsSection.rawValue {
    cell.textLabel?.text = channels[(indexPath as NSIndexPath).row].name
  }
  return cell
}

Để cho chắn chắn mọi thứ đều làm việc tốt, thêm đoạn code sau dưới thuộc tính:

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)  
  channels.append(Channel(id: "1", name: "Channel1"))
  channels.append(Channel(id: "2", name: "Channel2"))
  channels.append(Channel(id: "3", name: "Channel3"))
  self.tableView.reloadData()
}

Đoạn code trên là thêm một vài dự liệu dummy cho channel. Bây giờ build app màn hình channel sẽ như sau:

VI . Cấu trúc dữ liệu của Firebase

Firebase database là một NoSQL JSON data store. Tất cả mọi thứ trong Firebase database là JSON object, mỗi key của đối tượng JSON này đều có URL của nó.
Dưới đây là một ví dụ

{
  "channels": {
    "name": "Channel 1"
    "messages": {
      "1": { 
        "text": "Hey person!", 
        "senderName": "Alice"
        "senderId": "foo" 
      },
      "2": {
        "text": "Yo!",
        "senderName": "Bob"
        "senderId": "bar"
      }
    }
  }
}

V . Realtime Channel Synchronization

Đầu tiên là remove hàm viewDidAppear(_:) trong code đã add ở trên, sau đó thêm nhưng thuộc tính sau vào phía dưới

private lazy var channelRef: FIRDatabaseReference = FIRDatabase.database().reference().child("channels")
private var channelRefHandle: FIRDatabaseHandle?

channelRef will được dùng để lưu một tham chiếu tới danh sách các channel trong database;
Tiếp theo, cần query Firebase database và lấy danh sách các channel để hiển thị ra table view. Thêm đoạn code sau:

// MARK: Firebase related methods
private func observeChannels() {
  // Use the observe method to listen for new
  // channels being written to the Firebase DB
  channelRefHandle = channelRef.observe(.childAdded, with: { (snapshot) -> Void in // 1
    let channelData = snapshot.value as! Dictionary<String, AnyObject> // 2
    let id = snapshot.key
    if let name = channelData["name"] as! String!, name.characters.count > 0 { // 3
      self.channels.append(Channel(id: id, name: name))
      self.tableView.reloadData()
    } else {
      print("Error! Could not decode channel data")
    }
  })
}

Tiếp theo thêm đoạn sau:

// MARK: View Lifecycle
override func viewDidLoad() {
  super.viewDidLoad()
  title = "RW RIC"
  observeChannels()
} 
deinit {
  if let refHandle = channelRefHandle {
    channelRef.removeObserver(withHandle: refHandle)
  }
}

Để tạo một Channel, thêm đoạn code sau vào IBAction đã tồi tại trong storyboard

// MARK :Actions 
@IBAction func createChannel(_ sender: AnyObject) {
  if let name = newChannelTextField?.text { // 1
    let newChannelRef = channelRef.childByAutoId() // 2
    let channelItem = [ // 3
      "name": name
    ]
    newChannelRef.setValue(channelItem) // 4
  }
}

Bây giờ, build và run app màn hình sẽ như sau

VI . Creating the Chat Interface

Mở file ChatViewController.swift và đoạn imports sau:

import Firebase
import JSQMessagesViewController

Thay subclass từ UIViewController sang JSQMessagesViewController:

final class ChatViewController: JSQMessagesViewController {
At the top of ChatViewController, define the following properties:
var channelRef: FIRDatabaseReference?
var channel: Channel? {
  didSet {
    title = channel?.name
  }
}

Trong file ChannelListViewController.swift, thêm đoạn sau:

// MARK: Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  super.prepare(for: segue, sender: sender)
  if let channel = sender as? Channel {
    let chatVc = segue.destination as! ChatViewController
    chatVc.senderDisplayName = senderDisplayName
    chatVc.channel = channel
    chatVc.channelRef = channelRef.child(channel.id)
  }
}

Tiếp theo, trong file LoginViewController.swift, thêm đoạn code sau:

// MARK: Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  super.prepare(for: segue, sender: sender)
  let navVc = segue.destination as! UINavigationController // 1
  let channelVc = navVc.viewControllers.first as! ChannelListViewController // 2  
  channelVc.senderDisplayName = nameField?.text // 3
}

Trong file ChatViewController.swift, thêm đoạn code sau vào dưới viewDidLoad():

self.senderId = FIRAuth.auth()?.currentUser?.uid

Bây giờ build và run app màn hình sẽ như sau:

Kết luận

Dịch vụ Firebase của Google hỗ trợ rất tốt, nhờ đó mà việc phát triển các ứng dụng realtime hay Push Notification trở lên dễ dàng hơn. Giúp giảm đáng kể thời gian phát triển phần mềm cũng như tiết kiệm được khá nhiều nhân lực trong việc phát triển dự án phần mềm. Trong phần này tôi chỉ giới thiệu về tương tác với fire thông qua connect và tạo phòng để có thể chat được. Phần sau tôi xin được giới thiệu kỹ hơn về cách gửi message, image và hoàn thiện một ứng dụng chat realtime.

Tham khảo