• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

开始使用适用于iOS的Cloud Firestore

移动编码人员多年来一直在利用 Google 的 移动后端即服务 (MBaaS) 平台 firebase 实时数据库 ,帮助他们专注于为其应用程序构建功能,而不必担心后端基础架构和数据库。通过简化在云中存储和持久化数据以及处理身份验证和安全性,Firebase 允许编码人员专注于客户端。 

去年,Google 宣布了另一个后端数据库解决方案 Cloud Firestore,它从头开始构建,承诺提供更高的可扩展性和直观性。然而,这给它与谷歌已经存在的旗舰产品 Firebase Realtime database的关系带来了一些混乱。本教程将概述两个平台之间的差异以及每个平台的独特优势。通过构建一个简单的提醒应用程序,您将学习如何使用 Fi rest ore 文档引用,以及实时读取、写入、更新和删除数据。

开始使用适用于iOS的Cloud Firestore  第1张

本教程的目标

本教程将向您展示 Cloud Firestore。您将学习如何利用该平台进行实时数据库持久性和同步。我们将涵盖以下主题:

  • 什么是 Cloud Firestore

  • Firestore 数据模型

  • 设置 Cloud Firestore

  • 创建和使用 Cloud Firestore 引用

  • 从 Cloud Firestore 实时读取数据

  • 创建、更新和删除数据

  • 过滤和复合查询

假设知识

本教程假设您已经接触过 Firebase 并具有使用 swift 和 Xcode 进行开发的背景。

什么是 Cloud Firestore?

与 Firebase 实时数据库一样,Firestore 为移动和 Web 开发人员提供了跨平台的云解决方案,以实时保存数据,不受网络延迟或互联网连接的影响,并与 Google Cloud Platform 产品套件无缝集成。除了这些相似之处之外,还有一些明显的优点和缺点可以将两者区分开来。 

数据模型

在基本层面上,实时数据库将数据存储为一个大型的、单一的、分层的 JSON 树,而 Firestore 将数据组织在文档和集合以及子集合中。这需要较少的非规范化。在处理简单的数据需求时,将数据存储在一个 JSON 树中具有简单的好处;然而,当处理更复杂的分层数据时,它在规模上变得更加麻烦。 

离线支持

两种产品都提供离线支持,当有潜在网络连接或没有网络连接时,主动缓存队列中的数据——尽可能将本地更改同步回后端。除了移动应用之外,Firestore 还支持 Web 应用的离线同步,而实时数据库仅支持移动同步。

查询和交易 

实时数据库仅支持有限的排序和过滤功能——您只能在单个查询中对属性级别进行排序或过滤,但不能同时进行两者。查询也很深,这意味着它们会返回一个大的结果子树。该产品仅支持需要完成回调的简单写入和事务操作。 

另一方面,Firestore 引入了具有复合排序和过滤的索引查询,允许您组合操作来创建链式过滤器和排序。您还可以执行浅查询返回子集合,而不是使用实时数据库获得的整个集合。事务本质上是原子的,无论您发送批处理操作还是单个操作,事务都会自动重复直到结束。此外,实时数据库仅支持单个写入事务,而 Firestore 以原子方式提供批处理操作。

性能和可扩展性

正如您所期望的那样,实时数据库非常健壮且延迟低。但是,数据库仅限于单个区域,取决于区域可用性。另一方面,Firestore 将数据横向存储在多个区域和区域中,以确保真正的全球可用性、可扩展性和可靠性。事实上,谷歌已经承诺 Firestore 将比实时数据库更可靠。 

实时数据库的另一个缺点是限制为 100,000 个并发用户(单个数据库中 100,000 个并发连接和 1,000 次写入/秒),之后您必须对数据库进行分片(将数据库拆分为多个数据库)以支持更多用户. Firestore 会自动扩展多个实例,而无需您进行干预。 

Firestore 从头开始设计时就考虑到了可扩展性,它有一个新的示意图架构,可以跨多个区域复制数据,负责身份验证,并在其客户端 SDK 中处理其他与安全相关的事务。它的新数据模型比 Firebase 的更直观,更类似于其他类似的 Nosql 数据库解决方案,如mongodb,同时提供更强大的查询引擎。 

安全 

最后,正如您从我们之前的教程中了解的那样,实时数据库通过具有单独验证触发器的级联规则来管理安全性。这适用于 Firebase Database Rules,分别验证您的数据。另一方面,Firestore 利用 Cloud Firestore 安全规则和身份和访问管理 (IAM)提供更简单但更强大的安全模型,自动排除数据验证。

Firestore 数据模型

Firestore 是一个基于 NoSQL 文档的数据库,由文档集合组成,每个文档集合都包含数据。由于它是一个 NoSQL 数据库,因此您不会获得在关系数据库中可以找到的表、行和其他元素,而是可以在文档中找到的一组键/值对。 

您通过将数据分配给文档来隐式创建文档和集合,如果文档或集合不存在,它将自动为您创建,因为集合始终必须是根(第一个)节点。这是您将很快处理的项目的简单任务示例架构,包括任务集合,以及包含两个字段的大量文档,名称(字符串)和任务是否完成的标志(布尔值) .


开始使用适用于iOS的Cloud Firestore  第2张

让我们分解每个元素,以便您更好地理解它们。 

收藏品

与 SQL 世界中的数据库表同义,集合包含一个或多个文档。集合必须是架构中的根元素,并且只能包含文档,不能包含其他集合。但是,您可以引用一个文档,该文档又引用集合(子集合)。

开始使用适用于iOS的Cloud Firestore  第3张

在上面的图表中,一个任务由两个原始字段(名称和完成)以及一个由它自己的两个原始字段组成的子集合(子任务)组成。 

文件

文档由键/值对组成,其值具有以下类型之一: 

  • 原始字段(例如字符串、数字、布尔值)

  • 复杂的嵌套对象(原语列表或数组)

  • 子系列

嵌套对象也称为映射,在文档中可以如下表示。以下分别是嵌套对象和数组的示例:

ID: 2422892 //primitive
name: “Remember to buy milk” 
detail: //nested object
    notes: "This is a task to buy milk from the store"
	created: 2017-04-09
	due: 2017-04-10
done: false
notify: ["2F22-89R2", "L092-G623", "H00V-T4S1"]
...

有关支持的数据类型的更多信息,请参阅 Google 的 数据类型 文档。接下来,您将设置一个项目以使用 Cloud Firestore。

设置项目

如果您以前使用过 Firebase,那么您应该熟悉其中的很多内容。否则,您将需要在 Firebase 中创建一个帐户,并按照我们之前教程“ios 版 Firebase 身份验证入门”的“设置项目”部分中的说明进行操作  。 

要学习本教程,请克隆 教程项目 repo。 接下来,通过将以下内容添加到您的 Podfile来包含 Firestore 库:

pod 'Firebase/Core' 
pod 'Firebase/Firestore'

在您的终端中输入以下内容以构建您的库:

pod install

接下来,切换到 Xcode 并打开 .xcworkspace 文件。导航到AppDelegate.swift 文件并在方法中输入以下内容 application:didFinishLaunchingWithOptions: :

FirebaseApp.configure()

在浏览器中,转到 Firebase 控制台 并选择左侧 的数据库选项卡

开始使用适用于iOS的Cloud Firestore  第4张确保选择“以 测试模式启动”选项, 以便在我们进行试验时您不会遇到任何安全问题,并在您将应用程序投入生产时注意安全通知。您现在已准备好创建一个集合和一些示例文档。

添加集合和示例文档

首先Tasks,通过选择 添加收藏 按钮并命名收藏,创建一个初始收藏,如下图所示:

开始使用适用于iOS的Cloud Firestore  第5张对于第一个文档,您将文档 ID 留空,这将自动为您生成一个 ID。该文档将仅包含两个字段: name 和 done。

开始使用适用于iOS的Cloud Firestore  第6张保存文档,您应该能够确认收藏和文档以及自动生成的 ID:

开始使用适用于iOS的Cloud Firestore  第7张使用云中的示例文档设置数据库后,您就可以开始在 Xcode 中实现 Firestore SDK。

创建和使用数据库引用

在 Xcode 中打开 MasterViewController.swift 文件并添加以下行来导入库:

import Firebase

class MasterViewController: uitableviewController {
    @IBOutlet weak var addButton: UIBarButtonItem!
    
    private var documents: [DocumentSnapshot] = []
    public var tasks: [Task] = []
    private var listener : ListenerRegistration!
   ...

在这里,您只需创建一个***器变量,该变量将允许您在发生更改时实时触发与数据库的连接。您还将创建一个DocumentSnapshot包含临时数据快照的引用。

在继续使用视图控制器之前,创建另一个 swift 文件 Task.swift,它将代表您的数据模型:

import Foundation

struct Task{
    var name:String
    var done: Bool
    var id: String
    
    var dictionary: [String: Any] {
        return [
            "name": name,
            "done": done
        ]
    }
}

extension Task{
    init?(dictionary: [String : Any], id: String) {
        guard   let name = dictionary["name"] as? String,
            let done = dictionary["done"] as? Bool
            else { return nil }
        
        self.init(name: name, done: done, id: id)
    }
}

上面的代码片段包括一个方便属性(字典)和方法(init),这将使填充模型对象更容易。切换回视图控制器,并声明一个全局 setter 变量,它将基本查询约束到任务列表中的前 50 个条目。设置查询变量后,您还将删除***器,如下面的didSet属性所示:

fileprivate func baseQuery() -> Query {
        return Firestore.firestore().collection("Tasks").limit(to: 50)
    }
    
    fileprivate var query: Query? {
        didSet {
            if let listener = listener {
                listener.remove()
            }
        }
    }

override func viewDidLoad() {
        super.viewDidLoad()
        self.query = baseQuery()
    }

 override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.listener.remove()
    }

从 Cloud Firestore 实时读取数据

在文档引用到位后viewWillAppear(_animated: Bool),将您之前创建的***器与查询快照的结果相关联,并检索文档列表。这是通过调用 Firestore 方法完成的 query?.addSnapshotListener:

self.listener =  query?.addSnapshotListener { (documents, error) in
            guard let snapshot = documents else {
                print("Error fetching documents results: \(error!)")
                return
            }
            
            let results = snapshot.documents.map { (document) -> Task in
                if let task = Task(dictionary: document.data(), id: document.documentID) {
                    return task
                } else {
                    fatalError("Unable to initialize type \(Task.self) with dictionary \(document.data())")
                }
            }
            
            self.tasks = results
            self.documents = snapshot.documents
            self.tableView.reloadData()
            
        }

上面的闭包 snapshot.documents通过迭代地映射数组并将其包装到 Task 快照中每个数据项的新模型实例对象来分配 。因此,只需几行代码,您就成功地从云中读取了所有任务并将它们分配给全局tasks 数组。 

要显示结果,请填充以下委托方法: TableView

override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tasks.count
    }
    
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        let item = tasks[indexPath.row]
        
        cell.textLabel!.text = item.name
        cell.textLabel!.textColor = item.done == false ? UIColor.black : UIColor.lightGray
        
        return cell
    }

在这个阶段,构建并运行项目,在模拟器中你应该能够观察到实时出现的数据。通过 Firebase 控制台添加数据,您应该会看到它立即出现在应用模拟器中。 

开始使用适用于iOS的Cloud Firestore  第8张创建、更新和删除数据

从后端成功读取内容后,接下来您将创建、更新和删除数据。下一个示例将说明如何更新数据,使用一个人为的示例,其中应用程序只允许您通过点击单元格将项目标记为已完成。注意  闭包属性,它只是引用一个特定的文档 ID,更新字典中的每个字段:collection.document(item.id).updateData(["done": !item.done])

override func tableView(_ tableView: UITableView,
                            didSelectRowAt indexPath: IndexPath) {

        let item = tasks[indexPath.row]
        let collection = Firestore.firestore().collection("Tasks")

        collection.document(item.id).updateData([
            "done": !item.done,
            ]) { err in
                if let err = err {
                    print("Error updating document: \(err)")
                } else {
                    print("Document successfully updated")
                }
        }

        tableView.reloadRows(at: [indexPath], with: .automatic)
        
    }

要删除项目,请调用  方法:document(item.id).delete()

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if (editingStyle == .delete){
            let item = tasks[indexPath.row]
            _ = Firestore.firestore().collection("Tasks").document(item.id).delete()
        }

    }

创建一个新任务将涉及在 Storyboard 中添加一个新按钮并将其连接IBAction到视图控制器,创建一个addTask(_ sender:) 方法。当用户按下按钮时,它会弹出一个警告表,用户可以在其中添加新的任务名称:

collection("Tasks").addDocument
    (data: ["name": textFieldReminder.text ?? 
        "empty task", "done": false])

通过输入以下内容完成应用程序的最后一部分:

@IBAction func addTask(_ sender: Any) {
        
        let alertVC : UIAlertController = UIAlertController(title: "New Task", message: "What do you want to remember?", preferredStyle: .alert)
        
        alertVC.addTextField { (UITextField) in
            
        }
        
        let cancelAction = UIAlertAction.init(title: "Cancel", style: .destructive, handler: nil)
        
        alertVC.addAction(cancelAction)
        
        //Alert action closure
        let addAction = UIAlertAction.init(title: "Add", style: .default) { (UIAlertAction) -> Void in
            
            let textFieldReminder = (alertVC.textFields?.first)! as UITextField
            
            let db = Firestore.firestore()
            var docRef: DocumentReference? = nil
            docRef = db.collection("Tasks").addDocument(data: [
                "name": textFieldReminder.text ?? "empty task",
                "done": false
            ]) { err in
                if let err = err {
                    print("Error adding document: \(err)")
                } else {
                    print("Document added with ID: \(docRef!.documentID)")
                }
            }
            
        }
    
        alertVC.addAction(addAction)
        present(alertVC, animated: true, completion: nil)
        
    }

再次构建并运行应用程序,当模拟器出现时,尝试添加一些任务,以及将一些标记为已完成,最后通过删除一些任务来测试删除功能。您可以通过切换到 Firebase 数据库控制台并观察集合和文档来确认存储的数据已实时更新。

开始使用适用于iOS的Cloud Firestore  第9张

过滤和复合查询

到目前为止,您只使用了一个简单的查询,没有任何特定的过滤功能。要创建更健壮的查询,您可以使用whereField 子句按特定值进行过滤:

docRef.whereField(“name”, isEqualTo: searchString)

您可以使用 order(by: )和limit(to: ) 方法对查询数据进行排序和限制,如下所示:

docRef.order(by: "name").limit(5)

在 FirebaseDo 应用中,您已经使用limit了基本查询。在上面的代码片段中,您还使用了另一个功能,复合查询,其中 order 和 limit 都链接在一起。您可以根据需要链接任意数量的查询,例如在以下示例中:

docRef
    .whereField(“name”, isEqualTo: searchString)
	.whereField(“done”, isEqualTo: false)
	.order(by: "name")
	.limit(5)

结论

在本教程中,您探索了 Google 的新 MBaaS 产品 Cloud Firestore,并在此过程中创建了一个简单的任务提醒应用程序,该应用程序演示了在云中持久化、同步和查询数据是多么容易。您了解了 Firestore 与 Firebase 实时数据库相比的数据架构结构,以及如何实时读取和写入数据,以及更新和删除数据。您还学习了如何执行简单查询和复合查询,以及如何过滤数据。 


文章目录
  • 本教程的目标
    • 假设知识
  • 什么是 Cloud Firestore?
    • 数据模型
    • 离线支持
    • 查询和交易
    • 性能和可扩展性
    • 安全
  • Firestore 数据模型
    • 收藏品
    • 文件
  • 设置项目
    • 添加集合和示例文档
  • 创建和使用数据库引用
    • 从 Cloud Firestore 实时读取数据
    • 过滤和复合查询
  • 结论