使用Swift在Firebase中保存现有的JSON文件?

时间:2017-11-17 12:29:32

标签: json swift firebase

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

EDITED 好吧,澄清一下我在问什么。我有一个项目,其中包含一个文本文件,其中包含一个JSON数据。我最初将它添加到我的项目中,因为我没有使用Firebase,AWS等的经验。我只需要看看应用程序如何对数据做出反应。将来数据会显着增长,所以很明显我不打算将它保存在项目中。 问题是,我只想在Firebase中保存JSON数据。而且我不知道正确的方法:只需导入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数据的片段(例如,目前还没有完成,只有图像的字符串)。数据在整个应用程序中使用(在表格视图中,单独的视图等)。例如,我使用它来填充表视图,在mapView和其他UIViewController子类中使用它。此外,我需要一种过滤它并搜索它的能力。目前,我通过解码整个JSON并将其添加到数组中来做到这一点,然后,我过滤该数组。但我想,当您拥有大量带图像的数据时,这不是最佳选择。

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

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

1 个答案:

答案 0 :(得分:2)

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

蝙蝠的权利,通常有更好的方法在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节点内,写一个键:字符串的值对:uid的字符串:Leroy是这样的:

users
  uid_0: "Leroy"

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

将导致告诉Firebase在用户节点内写一个键:字符串的值对:uid字典:aDict。请注意,aDict是一个键:string对象本身是string:string(例如user_name:Leroy)

users
  uid_0
    user_name: "Leroy"

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