iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
Mobile Development

在 iOS 開發路上的大小事系列 第 28

【在 iOS 開發路上的大小事-Day28】透過 Firebase 來管理資料 (Cloud Firestore 篇) Part2

前情提要

昨天已經將環境設定好了,今天要來將新增、讀取、更新、刪除、排序功能實作出來

開始實作

設計留言的 Model

我們會需要一個 id 來記錄這是哪一筆留言,方便我們後面來處理
然後還需要記錄留言人的名字以及留言內容跟最後留言時間
所以就可以將這些東西設計成一個 struct

此外,後面我們還有透過時間來排序留言的需求
所以我們還需要讓這個 struct 符合 Comparable 的規範,所以

struct MessageModel: Comparable {
    static func < (lhs: MessageModel, rhs: MessageModel) -> Bool {
        return lhs.time < rhs.time
    }
    
    var id: String
    var name: String
    var content: String
    var time: String
}

建立變數

宣告兩個變數跟一個常數

let dataBase = Firestore.firestore() // 初始化 Firestore
var docRef: DocumentReference? = nil // 建立資料庫參考
var messageList = [MessageModel]() // 取得 Struct 內容

要加到 viewDidLoad 裡面的東西

override func viewDidLoad() {
    super.viewDidLoad()
    messageTableView.register(UINib(nibName: "FirestoreDatabaseCell", bundle: nil), forCellReuseIdentifier: "FirestoreDatabaseCell") // 因為我是用 Xib 設計畫面,所以要註冊一下
    messageTableView.delegate = self
    messageTableView.dataSource = self
    self.fetchMessageFromFirebase() // 這個後面會用到,先寫著
}

要加到送出 Button 的 IBAction 裡面的東西

@IBAction func sendMessageToFirestoreDatabase(_ sender: UIButton) {
    self.sendMessageToFirestore()
}

// MARK: - 送出留言到 Cloud Firestore
func sendMessageToFirestore() {
    let key = docRef?.documentID
    let message = [
        "id": key,
        "name": self.messagePeopleTF.text!,
        "content": self.messageContentTV.text!,
        "time": self.getSystemTime()
    ]
    docRef = dataBase.collection("messages").addDocument(data: message as [String : Any], completion: { error in
        guard error == nil else {
            CustomFunc.customAlert(title: "錯誤訊息", message: "Error adding document: \(String(describing: error))", vc: self, actionHandler: nil)
            return
        }
        CustomFunc.customAlert(title: "留言已送出!", message: "", vc: self, actionHandler: self.fetchMessageFromFirestore)
    })
    self.messagePeopleTF.text = ""
    self.messageContentTV.text = ""
}

從 Cloud Firestore Database 裡面讀取 / 抓取資料

// MARK: - 從 Cloud Firestore 抓取留言
func fetchMessageFromFirestore() {
    dataBase.collection("messages").getDocuments { snapshot, error in
        if let error = error {
            CustomFunc.customAlert(title: "錯誤訊息", message: "Error getting document: \(String(describing: error))", vc: self, actionHandler: nil)
        } else {
            self.messageList.removeAll()
            for messages in snapshot!.documents {
                let messageObject = messages.data(with: ServerTimestampBehavior.none)
//                    let messageID = messageObject["id"]
                let messageName = messageObject["name"]
                let messageContent = messageObject["content"]
                let messageTime = messageObject["time"]

                let message = MessageModel(
                    id: messages.documentID,
                    name: messageName as! String,
                    content: messageContent as! String,
                    time: messageTime as! String
                )
                self.messageList.append(message)
            }
            self.messageTableView.reloadData()
        }
    }
}

取得送出留言 / 更新留言的時間

// MARK: - 取得送出/更新留言的當下時間
func getSystemTime() -> String {
    let currectDate = Date()
    let dateFormatter: DateFormatter = DateFormatter()
    dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
    dateFormatter.locale = Locale.ReferenceType.system
    dateFormatter.timeZone = TimeZone.ReferenceType.system
    return dateFormatter.string(from: currectDate)
}

將留言呈現在 TableView 上面

extension CloudFirestoreDatabaseVC: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messageList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "FirestoreDatabaseCell", for: indexPath) as! FirestoreDatabaseCell
        cell.messagePeople.text = messageList[indexPath.row].name
        cell.messageContent.text = messageList[indexPath.row].content
        return cell
    }

更新留言

    // MARK: - 更新留言到 Cloud Firestore
    func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let editAction = UIContextualAction(style: .normal, title: "編輯") { action, view, completeHandler in
            let alertController = UIAlertController(title: "更新留言", message: "", preferredStyle: .alert)
            alertController.addTextField { textField in
                textField.text = self.messageList[indexPath.row].name
            }
            alertController.addTextField { textField in
                textField.text = self.messageList[indexPath.row].content
            }
            let updateAction = UIAlertAction(title: "更新", style: .default) { action in
                let updateMessage = [
                    "id": self.messageList[indexPath.row].id,
                    "name": alertController.textFields?[0].text!,
                    "content": alertController.textFields?[1].text!,
                    "time": self.getSystemTime()
                ]
                DispatchQueue.main.async {
                    self.dataBase.collection("messages").document("\(String(describing: self.messageList[indexPath.row].id))").updateData(updateMessage as [AnyHashable : Any])
                    CustomFunc.customAlert(title: "留言更新成功!", message: "", vc: self, actionHandler: self.fetchMessageFromFirestore)
                }
            }
            let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: nil)
            alertController.addAction(updateAction)
            alertController.addAction(cancelAction)
            self.present(alertController, animated: true)
            completeHandler(true)
        }
        let leadingSwipeAction = UISwipeActionsConfiguration(actions: [editAction])
        editAction.backgroundColor = UIColor(red: 0.0/255.0, green: 127.0/255.0, blue: 255.0/255.0, alpha: 1.0)
        return leadingSwipeAction
    }

刪除留言

    // MARK: - 從 Cloud Firestore 刪除留言
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "刪除") { action, view, completeHandler in
            DispatchQueue.main.async {
                self.dataBase.collection("messages").document("\(String(describing: self.messageList[indexPath.row].id))").delete { error in
                    if let error = error {
                        CustomFunc.customAlert(title: "錯誤訊息", message: "Error removing document: \(String(describing: error))", vc: self, actionHandler: nil)
                    } else {
                        CustomFunc.customAlert(title: "已成功刪除留言!", message: "", vc: self, actionHandler: self.fetchMessageFromFirestore)
                    }
                }
            }
            completeHandler(true)
        }
        let trailingSwipeAction = UISwipeActionsConfiguration(actions: [deleteAction])
        return trailingSwipeAction
    }
}

要加到排序留言 Button 的 IBAction 裡面的東西

// MARK: - 留言排序
@IBAction func sortMessage(_ sender: UIButton) {
    self.sortMessageFromFirestore()
}

enum sortMode {
    case defaultSort // 預設排序 (從新到舊)
    case fromNewToOldSort // 從新到舊
    case fromOldToNewSort // 從舊到新
}

func sortMessageFromFirestore() {
    let alertController = UIAlertController(title: "請選擇留言排序方式", message: "排序方式為送出/更新留言的時間早晚", preferredStyle: .actionSheet)
    let defaultAction = UIAlertAction(title: "預設排序", style: .default) { action in
        self.sortMessageList(sortMode: .defaultSort)
    }
    let fromNewToOldAction = UIAlertAction(title: "從新到舊", style: .default) { action in
        self.sortMessageList(sortMode: .fromNewToOldSort)
    }
    let fromOldToNewAction = UIAlertAction(title: "從舊到新", style: .default) { action in
        self.sortMessageList(sortMode: .fromOldToNewSort)
    }
    let closeAction = UIAlertAction(title: "關閉", style: .cancel, handler: nil)
    alertController.addAction(defaultAction)
    alertController.addAction(fromNewToOldAction)
    alertController.addAction(fromOldToNewAction)
    alertController.addAction(closeAction)
    self.present(alertController, animated: true)
}

func sortMessageList(sortMode: sortMode) {
    if (sortMode == .defaultSort || sortMode == .fromNewToOldSort) {
        self.messageList.sort(by: >)
    } else if (sortMode == .fromOldToNewSort) {
        self.messageList.sort(by: <)
    }
    self.messageTableView.reloadData()
}

成果

本篇的範例程式碼:

  1. MessageModel.swift:Github
  2. CloudFirestoreDatabaseVC.swift:Github

上一篇
【在 iOS 開發路上的大小事-Day27】透過 Firebase 來管理資料 (Cloud Firestore 篇) Part1
下一篇
【在 iOS 開發路上的大小事-Day29】淺談 iOS 的 Background Modes
系列文
在 iOS 開發路上的大小事30

尚未有邦友留言

立即登入留言