【问题标题】:Populate tableview with Firebase realtime database使用 Firebase 实时数据库填充 tableview
【发布时间】:2020-06-06 14:23:01
【问题描述】:

目前我从位于 http 服务器上的 json 文件获取数据

if let url = NSURL(string: "https://test.com/test") {
                do {
                    let data = try Data(contentsOf: url as URL)
                    let decoder = JSONDecoder()
                    jsonData = try decoder.decode(TestList.self, from: data)
                } catch {
                    print("error:\(error)")
                }

struct  TestList: Decodable {
        enum CodingKeys: String, CodingKey {
            case items
        }
        let items: [Item]
    }

struct Item: Decodable {
            var item_type: String?
            //...
            enum CodingKeys: String, CodingKey {
            case item_type
               //...
         }
            init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.item_type = try? container.decode(String.self, forKey: .mtype)
            //...        
            }
         }

一切正常,但当 我切换到 firebase 实时数据库并获得相同的 json 数据

Database.database().reference().child("items").observeSingleEvent(of: .value, with: { snapshot in
            guard let value = snapshot.value else { return }
            do {
                self.jsonData = try FirebaseDecoder().decode(TestList.self, from: value)
            } catch let error {
                print(error)
            }
        })

我的 JSON:

{
  "items": [
    {
      "item_type": "Rap",
      "1": "Kastro",
      "2": "EDI",
      "3": "Noble",
      "4": ""
    },
    {
      "item_type": "Rock",
      "1": "Nickelback",
      "2": "",
      "3": "",
      "4": ""
    },
    {
      "item_type": "Pop",
      "1": "Ringo",
      "2": "",
      "3": "",
      "4": ""
    }
  ]
}

收到此消息:

typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [], debugDescription: "不是 字典”,基础错误:无))

如何解决这个错误?

【问题讨论】:

  • Firebase 实时数据库不能直接使用 JSON,但是有一个很棒的库可以帮助您:github.com/alickbass/CodableFirebase
  • 我试过了,但它不能与 Decodable 一起工作
  • 你能显示你得到的 Json 响应吗?因为你得到的响应是一个字典,而这里的 testlist 是一个数组。
  • 是的,但为什么当 json 文件位于 http 服务器上时它可以工作?

标签: json swift firebase-realtime-database


【解决方案1】:

我之前遇到过这个问题,这就是我解决它的方法。所以假设这就是我的数据库的样子。注意命名约定,在本例中为蛇形。

假设我想从这个快照创建一个用户数组并填充我的 tableView。这很棘手,因为 snapshot.value 并不是真正的 JSON,它的值为 Any。这可能是您的应用程序崩溃或您的案例类型不匹配的原因。

让我们为我们的用户创建一个模型。它看起来像这样。

class User: Codable {
  var firstName: String
  var lastName: String
  var ageNumber: Int

  enum CodingKeys: String, CodingKey {
    case firstName,lastName
    case ageNumber = "age"
  }
}

所以让我指出一些我们非常快的事情。在我的 firebase 数据库中,用户属性看起来像这样“first_name”、“age”、“last_name”。在我的用户模型中,它看起来像“firstName”、“ageNumber”、“lastName”。所以我不得不添加编码键,因为年龄而不是因为名字或姓氏。当我在解码器上设置 keyCodingStrategy 时,名字和姓氏会自行更改。

这就是代码的样子。留下一些 cmets 以便更好地解释代码的作用。

var items = [Item]()
override func viewDidLoad() {
    super.viewDidLoad()
    // Use your database reference here.
    let ref = Database.database().reference(withPath: "users")

    ref.observe(.value) { (snapshot) in
        //First create a dict out of the snapshot value
        guard let dict = snapshot.value as? [String: Any] else { return }

        //Create a decoder this is why I don't need to chage the first_name to firstName
        //inside my coding keys
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase

        //The key is the UID and the value is what we need to create a new users
        dict.forEach { (key, value) in
            do {
                //Create new user and add it to our users array
                //We convert the value to data for the decoder with this line of code
                let testListData = try JSONSerialization.data(withJSONObject: value, options: .prettyPrinted)
                let testList = try decoder.decode(TestList.self, from: testListData)
                testList.items.forEach({ (item) in
                    self.items.append(item)
                })
            }
            catch let err {
            print(err.localizedDescription)
            }
        }
        //Reload Table View
    }
}

希望这会有所帮助。

已编辑

所以基于您提供的 JSON。您必须创建类似这样的结构。

struct TestList: Codable {
  var items: [Item]
}

struct Item: Codable {
  var itemType: String
  var one: String
  var two: String
  var three: String
  var four: String

  enum CodingKeys: String, CodingKey {
    case itemType
    case one = "1"
    case two = "2"
    case three = "3"
    case four = "4"
  }
}

希望这能解决您的问题。

【讨论】:

  • 为了更清楚,我添加了我的 Json,你能帮帮我吗?
  • @Jay 刚刚更新了答案,希望对您有所帮助。如果有,请告诉我。
  • 我改变了 testList.items.forEach({ (item) in testList.items.append(item) }) 否则 items.append(item) 说“使用未解析的标识符‘items’”并保护 let dict = snapshot.value as? [String: Any] else { return } 执行 else 并返回
  • 您应该使用您创建的数组来添加项目,而不是 items.append(item)。
  • let ref = Database.database().reference(withPath: "users") 我已更改为 let ref = Database.database().reference(withPath: "items") 代码编译时没有错误,但看守让 dict = snapshot.value as? [String: Any] else { return } 返回时退出,下面的代码未执行
猜你喜欢
  • 1970-01-01
  • 2018-11-22
  • 2016-11-09
  • 2020-05-28
  • 2021-12-29
  • 1970-01-01
  • 2021-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多