【在 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

<<:  Day40 ( 游戏设计 ) 反弹球 ( 乒乓球 )

>>:  Day40 ( 电子元件 ) 旋钮控制 LED 亮度

[Day08] 流程判断:if else 与 switch

「如果怎样就做某件事,否则做另一件事。」这个在聊天时都会拿来用的“程序语法”,来看看在 JS 上怎麽...

[序章] 自然语言处理初探

前言 生活在网际网路以及智慧型手机普及的今天,与外国朋友聊天、出国旅行、与国外客户开商务会议,纵使不...

Day20 用python写UI-聊聊Combobox

Combobox就是组合框,是tkinter.ttk的空件,所以要先import才可以用。他跟ope...

【Day29】this - DOM

今天要来讲解 DOM 与 this 的关系, 对於 DOM 的操作有两种方式, 第一种是直接将方法写...

[Day 2] 差异性安装

如需在地端环境操作 那需要去理解 什麽是node JS 什麽是NPM 需要参照 本地安装 使用 np...