【问题标题】:Saving an existing JSON file in Firebase using Swift?使用 Swift 在 Firebase 中保存现有的 JSON 文件?
【发布时间】:2018-05-01 05:35:52
【问题描述】:

我有一个 JSON 文件,本地保存在我的项目中。现在我很好奇将它保存在 Firebase 中。我知道 Firebase 以 NoSQL 格式(作为 JSON)保存数据。我看过几个关于向 Firebase 添加文件的教程(经过一些操作,比如在示例 iOS 应用程序中点击一个按钮)。但是是否有可能在firebase中保存整个JSON文件本身(不仅仅是它的块),最初(不是在点击某个按钮之后),然后加载该文件,因为在我的项目中用户不能自己添加文件?应用程序应从 JSON 文件加载数据。 我是 Firebase 的新手,所以我几乎没有使用它的经验。如果您有任何建议或知道如何执行此操作,我们将不胜感激。

已编辑 好吧,澄清我在问什么。我有一个项目,其中包含一个文本文件,其中包含一个 JSON 数据。最初,我将它添加到我的项目中,因为我没有使用 Firebase、AWS 等的经验。我只需要看看应用程序如何对数据做出反应。将来数据会显着增长,所以显然我不会将它保存在项目中。 问题是,我只想将 JSON 数据保存在 Firebase 中。而且我不知道这样做的正确方法:只需导入 JSON 或从头开始编写它(以编程方式或在控制台中)。另外,我想知道这是否是一个好主意,因为这是我第一次使用在线数据库,很多人说 Firebase 是最好的数据库。我对所有这些东西都很陌生,这就是我问这些问题的原因。

EDITED_2

{
"list": {
    "restaurants": [{
                    "id": "231311234342bn123",
                    "name": "Grand Royal",
                    "suggested": true,
                    "address": {
                    "longitude": "30.31527",
                    "latitude": "59.93688",
                    "formatted": ["Nevskiy prospekt 10, Saint Petersburg",
                                  "Russian Federation"
                                  ]
                    },
                    "tags": ["fish",
                             "Italian",
                             "Friends"
                             ],
                    "category": "$$$$",
                    "description": "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.",
                    "shortDescription": "an",
                    "collectionName": "Relaxing with friends",
                    "distance": "1324",
                    "country": "Russian Federation",
                    "Region": "North-West region",
                    "city": "Saint Petersburg"
                    },

                    {
                    "id": "123234344t5213",
                    "name": "N",
                    "image": "A",
                    "suggested": true,
                    "address": {
                    "longitude": "30.31821",
                    "latitude": "59.93605",
                    "formatted": ["Nevskiy prospekt 15, Saint Petersburg",
                                  "Russian Federation"
                                  ]
                    },
                    "tags": ["fish",
                             "french",
                             "family"
                             ],
                    "category": "$$$$",
                    "description": "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. ",
                    "shortDescription": "ab",
                    "collectionName": "Evening with a family",
                    "distance": "556",
                    "country": "Russian Federation",
                    "Region": "North-West region",
                    "city": "Saint Petersburg"
                    }]}}

这是我的初始 JSON 数据的 sn-p(它没有完成,例如,此时只有一个图像字符串)。数据在整个应用程序中使用(在表格视图、单独的视图等中)。例如,我用它来填充表格视图,在mapView 和其他UIViewController 子类中使用它。另外,我需要过滤和搜索它的能力。目前,我通过解码整个 JSON 并将其添加到数组中来做到这一点,然后过滤该数组。但我想,当您拥有大量图像数据时,这不是最佳选择。

还有一件事,在Core Data 中可以设置批处理请求,因此它最初只会获取一些数据量。它适用于表格视图,因为当您滚动它们时,Core Data 会自动获取新数据以填充单元格。是否可以使用 firebase 做类似的事情,或者我应该一次读取所有数据?

我还了解到 Firebase 不需要保存类似数组的数据,但有几种方法可以解决该问题。这对我很重要,因为我确实需要将部分数据放在数组中。

【问题讨论】:

  • 这里没有足够的信息来真正提供答案;不清楚您的项目中保存了一个 JSON 文件 是什么意思。 JSON 是一种数据结构而不是文件类型。您的意思是它是一个包含 JSON 数据的文本文件,并且您想将该文件存储在 Firebase 中吗?如果是这样,Storage 或 Cloud Firestore 将是更好的选择。如果您想在 Firebase 中存储 JSON 数据,这很容易。用例也不清楚;当您可以从数据库中加载数据时,为什么还要从文件中加载数据。如果您能澄清您的问题,我们会尽力提供答案。
  • @Jay,我已经编辑了我的问题。
  • 非常好。您能否提供您想要使用的 JSON 数据的 sn-p 并描述您想要使用该数据做什么。您如何构建 Firebase 很大程度上取决于您要如何处理这些数据。将其作为 TEXT 包含在您的问题中,我们将进行查看。
  • @Jay,完成。我添加了一个 sn-p 并尝试描述我需要对数据做什么。

标签: json swift firebase


【解决方案1】:

希望这将提供一些方向,请原谅长度。

正确地说,在 Firebase 中存储数据的方法通常比使用数组更好;数组不可搜索、不可编辑或可添加,如果您想更改某些内容,则需要覆盖整个数组。请参阅Arrays Are Evil

其次是在 Firebase 中的 Denormalizing Data Is Normal。这意味着当数据变得太“深”时,查询您想要的数据会变得更加困难。通常更平坦更好 - 取决于您想从 Firebase 中获得什么。

第三项是有 100 种不同的方式来处理您问题中的数据,我将介绍两种;一个从概念上演示如何编写数据,第二个可能是更好的选择。

以下是如何以蛮力的方式写入数据

let addressDict = [
    "longitude": "30.31527",
    "latitude": "59.93688",
    "formatted1": "Nevskiy prospekt 10, Saint Petersburg",
    "formatted2": "Russian Federation"
]

let tagsDict = [
    "fish": true,
    "italian": true,
    "friends": true
]

let restaurantDict = [
    "name": "Grand Royal",
    "suggested": true,
    "address": addressDict,
    "tags": tagsDict,
    "category": "$$$$",
    "description": "Lorem ipsum dolor si.",
    "shortDescription": "an",
    "collectionName": "Relaxing with friends",
    "distance": "1324",
    "country": "Russian Federation",
    "Region": "North-West region",
    "city": "Saint Petersburg"
    ] as [String : Any]

let restaurantsRef = self.ref.child("restaurants").childByAutoId()
restaurantsRef.setValue(restaurantDict)

运行此代码将生成与您的结构相似的 JSON 结构。请注意,餐厅键 (KzEIkF6nxgWH1nA8B2D) 是在写入数据时自动创建的,因此这就是在其他结构中通过其键来引用餐厅的方式。

"restaurants" : {
    "-KzEIkF6nxgWH1nA8B2D" : {
      "Region" : "North-West region",
      "address" : {
        "formatted1" : "Nevskiy prospekt 10, Saint Petersburg",
        "formatted2" : "Russian Federation",
        "latitude" : "59.93688",
        "longitude" : "30.31527"
      },
      "category" : "$$$$",
      "city" : "Saint Petersburg",
      "collectionName" : "Relaxing with friends",
      "country" : "Russian Federation",
      "description" : "Lorem ipsum dolor si.",
      "distance" : "1324",
      "name" : "Grand Royal",
      "shortDescription" : "an",
      "suggested" : true,
      "tags" : {
        "fish" : true,
        "friends" : true,
        "italian" : true
      }
    }
  },

查询所有 $$$$ 餐厅:

//print all restaurants with $$$$
let restRef = self.ref.child("restaurants")
let query = restRef.queryOrdered(byChild: "category").queryEqual(toValue: "$$$$")
query.observeSingleEvent(of: .value) { snapshot in
    for child in snapshot.children {
        let snap = child as! DataSnapshot
        print(snap)
    }
}

虽然我们可以查询所有具有类别 == "$$$$" 的餐厅,但地址和标签节点太深(实际上并不准确,请参阅 Deep Query,但我们会使用它对于这个例子,因为这个概念是合理的)。

所以更好的结构是

"restaurants" : {
    "-KzEIkF6nxgWH1nA8B2D" : {
      "Region" : "North-West region",
      "category" : "$$$$",
      "city" : "Saint Petersburg",
      "collectionName" : "Relaxing with friends",
      "country" : "Russian Federation",
      "description" : "Lorem ipsum dolor si.",
      "distance" : "1324",
      "name" : "Grand Royal",
      "shortDescription" : "an",
      "suggested" : true,
    }
  },

"addresses"
   "-KzEIkF6nxgWH1nA8B2D"
        "formatted1" : "Nevskiy prospekt 10, Saint Petersburg",
        "formatted2" : "Russian Federation",
        "latitude" : "59.93688",
        "longitude" : "30.31527"

"tags"
   "-KzEIkF6nxgWH1nA8B2D"
        "fish" : true,
        "friends" : true,
        "italian" : true

以及创建它的代码:

let restDict = [
    "name": "Jay's Eatery",
    "category": "$$$$"
]

let addressDict = [
    "formatted1": "Anytown"]

let tagsDict = [
    "fish": true,
    "friends": true,
    "italian": true
]

let restRef = self.ref.child("restaurants")
let addressesRef = self.ref.child("addresses")
let tagsRef = self.ref.child("tags")

let thisRestaurantRef = restRef.childByAutoId()
thisRestaurantRef.setValue(restDict, withCompletionBlock: { (error, snapshot) in
    if error == nil {
        let thisRestaurantKey = snapshot.key
        addressesRef.child(thisRestaurantKey).setValue(addressDict)
        tagsRef.child(thisRestaurantKey).setValue(tagsDict)
    }
})

现在您可以使用上面的查询轻松查询带有鱼标签、以 59 开头的纬度或 Grand Royal 名称的餐厅。

如您所见,当数据存储在 Firebase 中时,不涉及编码或解码。

是的,Firebase 可以批量加载数据以填充数组以用作 tableView 数据源

最佳做法是创建在您的代码中向用户表示数据的类对象,并在后面利用 Firebase 来存储该数据。

class RestaurantClass {
    key = "" //"-KzEIkF6nxgWH1nA8B2D"
    name = "" //"Grand Royal
    etc

    func init(withSnapshot: DataSnapshot) {
      //populate the class vars from the snapshot
}

那么对于 $$$$ 餐厅的查询将是这样的:

let restRef = self.ref.child("restaurants")
let query = restRef.queryOrdered(byChild: "category").queryEqual(toValue: "$$$$")
query.observeSingleEvent(of: .value) { snapshot in
    for child in snapshot.children {
        let snap = child as! DataSnapshot
        let rest = RestaurantClass(withSnap: snap)
        self.myTableViewDataSourceArray.append(rest)
    }
    self.myTableViewDataSourceArray.reloadData()
}

还有很多,但这应该会让你朝着正确的方向前进。

这个答案的要点是利用 Firebase 在后端存储原始数据 - 如果数据结构正确,它的速度非常快、可扩展和可维护。在代码中使用填充了后端数据的类作为数据源并将数据呈现给用户。

最后一点是,如果您正在处理位置,您将需要一个工具来简化它。查看GeoFire 了解更多信息。

编辑:

为了澄清评论。

您不会像为类或结构创建属性那样在代码中完全创建 Firebase 数据结构,而且它与带有标题的空 SQL 表完全不同。更重要的是您如何告诉 Firebase 如何编写该数据。比如:

ref.child("users").child("uid_0").setValue("Leroy")

告诉 Firebase 在 users 节点中,写一个 key: value pair of a string: string of uid: Leroy like this:

users
  uid_0: "Leroy"

let aDict = ["user_name": "Leroy"]
ref.child("users").child("uid_0").setValue(aDict)

将导致告诉 Firebase 在用户节点内写入一个键:字符串的值对:uid 的字典:aDict。注意 aDict 是一个 key: value 对本身的 string: string (e.g user_name: Leroy)

users
  uid_0
    user_name: "Leroy"

我喜欢认为 Firebase 是一个大的键字典:值对,其中键始终是字符串(避免使用数组!),值是任何类型,可以是字符串、整数等或其他字典。

【讨论】:

  • 感谢您的回答。它确实涵盖了很多内容并且非常有帮助。我想澄清一点:这些数据结构是用 Xcode 编写的,对吧?所以,据我所知,它们是写在一个简单的 Swift 文件中(而不是空白的 TEXT 文件)。第二个问题:第二种结构包含三个不同的字典,用于单个餐厅(它们具有相同的键),它们是如何创建的(对于第一种数据结构,它的创建方式非常直观,但是对于第二个,我不知道它是如何创建的)?请你说清楚好吗?
  • @TigranIskandaryan 好问题。我已经更新了我的答案;添加了代码以创建建议的结构和编辑以获取更多信息。澄清一下:Firebase 结构未写入任何文件。结构是根据代码的编写方式创建的,这些结构是字典样式的键:值对。它不像 SQL,您可以在其中创建一个带有标题的空表并在以后填写它。如果一个节点不存在,它将使用 setValue 创建。另请参阅 Data Structure 中的 childByAutoId 为您的节点创建唯一键。
  • 感谢您的帮助。您的回答确实让我明白了很多关于 Firebase 的事情。再次感谢您!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 2018-10-18
相关资源
最近更新 更多