更新Firestore数据库会导致iOS崩溃

时间:2018-04-03 22:05:15

标签: ios swift firebase google-cloud-firestore

当我使用任何新字段更新firebase firestore数据库时,它会立即杀死运行使用该数据的任何应用程序,并在下面的代码中显示致命错误。

我得到的错误是“fatalError:”无法用字典初始化类型餐馆[(名称:“测试”,可用性:“测试”,类别:“测试”)]

我希望能够在不让应用程序崩溃的情况下更新它。如果发生这种情况,他们必须删除并重新安装应用程序以使其再次工作,所以我认为它以某种方式在本地存储数据,但我找不到位置。

如何在不崩溃的情况下重置数据或重新加载?

抛出错误的文件(加载表数据时):

fileprivate func observeQuery() {
    stopObserving()
    guard let query = query else { return }
    stopObserving()
    listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
        guard let snapshot = snapshot else {
            print("Error fetching snapshot results: \(error!)")
            return
        }
        let models = snapshot.documents.map { (document) -> Restaurant in
            if let model = Restaurant(dictionary: document.data()) {
                return model
            } else {
                // Don't use fatalError here in a real app.
                fatalError("Unable to initialize type \(Restaurant.self) with dictionary \(document.data())")
            }
        }
        self.restaurants = models
        self.documents = snapshot.documents

        if self.documents.count > 0 {
            self.tableView.backgroundView = nil
        } else {
            self.tableView.backgroundView = self.backgroundView
        }

        self.tableView.reloadData()
    }


  }

还有Restaurant.swift文件:

import Foundation 

struct Restaurant {

  var name: String
  var category: String // Could become an enum
  var availability: String // from 1-3; could also be an enum
    var description: String

  var dictionary: [String: Any] {
    return [
      "name": name,
      "category": category,
      "availability": availability,
      "description": description
    ]
  }

}

extension Restaurant: DocumentSerializable {

    //Cities is now availability
  static let cities = [
    "In Stock",
    "Back Order",
    "Out of Stock"
  ]

  static let categories = [
    "Rock", "Boulder", "Grass", "Trees", "Shrub", "Barrier"
  ]

  init?(dictionary: [String : Any]) {
    guard let name = dictionary["name"] as? String,
        let category = dictionary["category"] as? String,
        let availability = dictionary["availability"] as? String,
        let description = dictionary["description"] as? String
    else { return nil }

    self.init(name: name,
              category: category,
              availability: availability,
              description: description
    )
  }

}

带有Document.Serializable代码的本地收集文件:

import FirebaseFirestore

// A type that can be initialized from a Firestore document.
protocol DocumentSerializable {
  init?(dictionary: [String: Any])
}

final class LocalCollection<T: DocumentSerializable> {

  private(set) var items: [T]
  private(set) var documents: [DocumentSnapshot] = []
  let query: Query

  private let updateHandler: ([DocumentChange]) -> ()

  private var listener: ListenerRegistration? {
    didSet {
      oldValue?.remove()
    }
  }

  var count: Int {
    return self.items.count
  }

  subscript(index: Int) -> T {
    return self.items[index]
  }

  init(query: Query, updateHandler: @escaping ([DocumentChange]) -> ()) {
    self.items = []
    self.query = query
    self.updateHandler = updateHandler
  }

  func index(of document: DocumentSnapshot) -> Int? {
    for i in 0 ..< documents.count {
      if documents[i].documentID == document.documentID {
        return i
      }
    }

    return nil
  }

  func listen() {
    guard listener == nil else { return }
    listener = query.addSnapshotListener { [unowned self] querySnapshot, error in
      guard let snapshot = querySnapshot else {
        print("Error fetching snapshot results: \(error!)")
        return
      }
      let models = snapshot.documents.map { (document) -> T in
        if let model = T(dictionary: document.data()) {
          return model
        } else {
          // handle error
          fatalError("Unable to initialize type \(T.self) with local dictionary \(document.data())")
        }
      }
      self.items = models
      self.documents = snapshot.documents
      self.updateHandler(snapshot.documentChanges)
    }
  }

  func stopListening() {
    listener = nil
  }

  deinit {
    stopListening()
  }
}

1 个答案:

答案 0 :(得分:1)

  

fatalError:&#34;无法用字典初始化餐厅类型[(名称:&#34;测试&#34;,可用性:&#34;测试&#34;,类别:&#34;测试&#34; )]

看起来非常简单 - 该词典没有足够的信息来创建一个Restaurant对象。

错误来自

if let model = Restaurant(dictionary: document.data()) {
    return model
} else {
    // Don't use fatalError here in a real app.
    fatalError("Unable to initialize type \(Restaurant.self) with dictionary \(document.data())")
}

因为初始值设定项返回nil值,来自:

init?(dictionary: [String : Any]) {
 guard let name = dictionary["name"] as? String,
    let category = dictionary["category"] as? String,
    let availability = dictionary["availability"] as? String,
    let description = dictionary["description"] as? String
 else { return nil }

 self.init(name: name,
          category: category,
          availability: availability,
          description: description
 )
}

因为您的guard返回nil,因为您在字典中没有 description 键。

要解决此问题,请在字典中添加 description 键,或者在缺少密钥时更改初始值设定项以使用默认说明。

例如,这是您的初始值设定项,重写为使用默认描述,用于描述条目缺失时

init?(dictionary: [String : Any]) {
 guard let name = dictionary["name"] as? String,
    let category = dictionary["category"] as? String,
    let availability = dictionary["availability"] as? String
 else { return nil }

 let description = dictionary["description"] as? String
 let defaultDescription: String = description ?? "No Description"

 self.init(name: name,
          category: category,
          availability: availability,
          description: defaultDescription
 )
}