【问题标题】:Get Firebase document objects as Swift object将 Firebase 文档对象作为 Swift 对象获取
【发布时间】:2018-08-11 17:08:58
【问题描述】:

我尝试为 iOS 实现一个简单的购物清单 swift 应用程序作为个人项目。我确实遵循了youtube 上的 iOS 指南。

我的问题是如何将 Item 对象从 firebase 解析为我的 ShoppingListItem swift 对象?如果我执行以下代码,它不会显示任何错误消息,但也不会显示任何结果。如果我取消注释所有“项目”行,它会显示没有项目信息的预期结果。

这是我的firebase firestore structure / example object的firebase控制台的截图

提前致谢!


ShoppingListItem.swift

import Foundation
import FirebaseFirestore

protocol DocumentSerializable {
    init?(dictionary: [String: Any])
}

struct ShoppingListItem {
    var shoppingItemID: String
    var priority: Int
    var quantity: Int
    var item: Item

    var dictionary: [String: Any] {
        return [
            "shoppingItemID": shoppingItemID,
            "priority": priority,
            "quantity": quantity,
            "item": item,
        ]
    }
}

extension ShoppingListItem: DocumentSerializable {

    init?(dictionary: [String : Any]) {
        guard let shoppingItemID = dictionary["shoppingItemID"] as? String,
            let priority = dictionary["priority"] as? Int,
            let quantity = dictionary["quantity"] as? Int,
            let item = dictionary["item"] as? Item
        else { return nil }


        self.init(shoppingItemID: shoppingItemID, priority: priority, quantity: quantity, item: item)
    }
}

struct Item {

    var itemID: String
    var lastPurchase: String
    var name: String
    var note: String
    var picturePath: String

    var dictionary: [String: Any] {
        return [
            "itemID": itemID,
            "lastPurchase": lastPurchase,
            "name": name,
            "note": note,
            "picturePath": picturePath,
        ]
    }
}

extension Item: DocumentSerializable {

    init?(dictionary: [String : Any]) {
        guard let itemID = dictionary["itemID"] as? String,
            let lastPurchase = dictionary["lastPurchase"] as? String,
            let name = dictionary["name"] as? String,
            let note = dictionary["note"] as? String,
            let picturePath = dictionary["picturePath"] as? String else { return nil }

        self.init(itemID: itemID, lastPurchase: lastPurchase, name: name, note: note, picturePath: picturePath)
    }
}

在 TableViewController.swift 中获取数据调用

        db.collection("shoppingList").getDocuments(){
            querySnapshot, error in

            if let error = error {
                print("error loading documents \(error.localizedDescription)")
            } else{
                self.shoppingArray = querySnapshot!.documents.flatMap({ShoppingListItem(dictionary: $0.data())})

                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }

        }

【问题讨论】:

    标签: ios swift firebase google-cloud-firestore


    【解决方案1】:

    我使用了 Codable 协议。

    我将其用作可编码协议的扩展:

    extension Encodable {
      /// Returns a JSON dictionary, with choice of minimal information
      func getDictionary() -> [String: Any]? {
        let encoder = JSONEncoder()
    
        guard let data = try? encoder.encode(self) else { return nil }
        return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any]
        }
      }
    }
    

    然后我用这个来解码:

    extension Decodable {
      /// Initialize from JSON Dictionary. Return nil on failure
      init?(dictionary value: [String:Any]){
    
        guard JSONSerialization.isValidJSONObject(value) else { return nil }
        guard let jsonData = try? JSONSerialization.data(withJSONObject: value, options: []) else { return nil }
    
        guard let newValue = try? JSONDecoder().decode(Self.self, from: jsonData) else { return nil }
        self = newValue
      }
    }
    

    使您的两个结构符合 Codable(首先是 Item,然后是 ShoppingListItem)。当然,这可能不适用于 Firestore 中存储的现有数据。我会首先通过getDictionary()(在新集合中)将数据放入 Firestore,然后尝试将其读回您的 tableView。

    【讨论】:

    • 感谢您的回答!我确实删除了协议和扩展并将您的代码添加到我的ShoppingListItem.swift。它现在具有以下结构:imports - your extensions - struct Item - struct ShoppingListItem。就像您建议的那样,我确实将新数据放入了新集合中,并且确实有效!但是如何对数据进行编码?我上面问题中的代码不再起作用,并且出现以下消息:“Missing argument for parameter 'priority' in call”
    • 我不明白如何编码我的数据?您是在问应该如何将数据写入 Firestore?我假设你有一些看起来像documentReference.setData(shoppingListItem.dictionary) { /** error closure */ } 的代码。将dictionary 属性替换为getDictionary() 方法。
    • 对不起,我的意思是解码。我如何从 Firestore 中检索数据,因为这不再起作用:self.shoppingArray = querySnapshot!.documents.flatMap({ShoppingListItem(dictionary: $0.data())})
    • 您是否尝试导入没有“优先级”键的数据?如果是这样,那就是问题所在。默认导入要求所有属性都有键条目。
    • 不,这是编译器错误,而不是运行时错误。不知何故,它现在编译时没有错误,但即使快照中有五个文档(querySnapshot?.documents.count),它仍然会解码 0 个对象。您将如何将文档存储在数组中? ShoppingListItem(dictionary: $0.data()) 仍然正确还是需要 ShoppingListItem(from: $0.data() as! Decoder) 之类的东西?
    猜你喜欢
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 2019-10-15
    • 2013-09-16
    • 2018-12-15
    • 2020-04-02
    相关资源
    最近更新 更多