Can Swift 4's JSONDecoder be used with Firebase Realtime Database?

后端 未结 7 1635
有刺的猬
有刺的猬 2020-12-28 19:39

I am trying to decode data from a Firebase DataSnapshot so that it can be decoded using JSONDecoder.

I can decode this data fine when I use a URL to access it with a

相关标签:
7条回答
  • 2020-12-28 20:19

    I have created a library called CodableFirebase that provides Encoders and Decoders that are designed specifically for Firebase.

    So for the example above:

    import Firebase
    import CodableFirebase
    
    let item: GroceryItem = // here you will create an instance of GroceryItem
    let data = try! FirebaseEncoder().encode(item)
    
    Database.database().reference().child("pathToGraceryItem").setValue(data)
    

    And here's how you will read the same data:

    Database.database().reference().child("pathToGraceryItem").observeSingleEvent(of: .value, with: { (snapshot) in
        guard let value = snapshot.value else { return }
        do {
            let item = try FirebaseDecoder().decode(GroceryItem.self, from: value)
            print(item)
        } catch let error {
            print(error)
        }
    })
    
    0 讨论(0)
  • 2020-12-28 20:19

    If your data type is Codable you can use the following solution to decode directly. You do not need any plugin. I used the solution for Cloud Firestore.

    import Firebase
    import FirebaseFirestoreSwift
    
    
    let db = Firestore.firestore()
    let query = db.collection("CollectionName")
                .whereField("id", isEqualTo: "123")
    
    guard let documents = snapshot?.documents, error == nil else {
        return
    }
    
    if let document = documents.first {
        do {
            let decodedData = try document.data(as: ModelClass.self) 
            // ModelClass a Codable Class
    
        }
        catch let error {
            // 
        }
    }
    
    0 讨论(0)
  • 2020-12-28 20:30

    I've converted Firebase Snapshots using JSONDecoder by converting snapshots back to JSON in Data format. Your struct needs to conform to Decodable or Codable. I've done this with SwiftyJSON but this example is using JSONSerialization and it still works.

    JSONSnapshotPotatoes {
        "name": "Potatoes",
        "price": 5,
    }
    JSONSnapshotChicken {
        "name": "Chicken",
        "price": 10,
        "onSale": true
    }
    
    struct GroceryItem: Decodable {
        var name: String
        var price: Double
        var onSale: Bool? //Use optionals for keys that may or may not exist
    }
    
    
    Database.database().reference().child("grocery_item").observeSingleEvent(of: .value, with: { (snapshot) in
            guard let value = snapshot.value as? [String: Any] else { return }
            do {
                let jsonData = try JSONSerialization.data(withJSONObject: value, options: [])
                let groceryItem = try JSONDecoder().decode(GroceryItem.self, from: jsonData)
    
                print(groceryItem)
            } catch let error {
                print(error)
            }
        })
    

    Please note that if your JSON keys are not the same as your Decodable struct. You'll need to use CodingKeys. Example:

    JSONSnapshotSpinach {
        "title": "Spinach",
        "price": 10,
        "onSale": true
    }
    
    struct GroceryItem: Decodable {
        var name: String
        var price: Double
        var onSale: Bool?
    
        enum CodingKeys: String, CodingKey {
            case name = "title"
    
            case price
            case onSale
        }
    }
    

    You can find more information on this using Apple Docs here.

    0 讨论(0)
  • 2020-12-28 20:31

    Or you can use this solution for children

    extension DatabaseReference {
      func makeSimpleRequest<U: Decodable>(completion: @escaping (U) -> Void) {
        self.observeSingleEvent(of: .value, with: { snapshot in
            guard let object = snapshot.children.allObjects as? [DataSnapshot] else { return }
            let dict = object.compactMap { $0.value as? [String: Any] }
            do {
                let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
                let parsedObjects = try JSONDecoder().decode(U.self, from: jsonData)
                completion(parsedObjects)
            } catch let error {
                print(error)
            }
        })
      }
    }
    

    and use

    self.refPriceStatistics.child(productId).makeSimpleRequest { (parsedArray: [YourArray]) in
        callback(parsedArray)
    }
    
    0 讨论(0)
  • 2020-12-28 20:33

    You can use this library CodableFirebase or the following extension can be helpful.

    extension JSONDecoder {
    
    func decode<T>(_ type: T.Type, from value: Any) throws -> T where T : Decodable {
        do {
            let data = try JSONSerialization.data(withJSONObject: value, options: .prettyPrinted)
            let decoded = try decode(type, from: data)
            return decoded
    
        } catch {
            throw error
        }
    }
    
    0 讨论(0)
  • 2020-12-28 20:35

    No. Firebase returns a FIRDataSnapshot that can't be decodable. You can use this structure however, which is pretty simple and easy to understand:

    struct GroceryItem {
      
      let key: String
      let name: String
      let addedByUser: String
      let ref: FIRDatabaseReference?
      var completed: Bool
      
      init(name: String, addedByUser: String, completed: Bool, key: String = "") {
        self.key = key
        self.name = name
        self.addedByUser = addedByUser
        self.completed = completed
        self.ref = nil
      }
      
      init(snapshot: FIRDataSnapshot) {
        key = snapshot.key
        let snapshotValue = snapshot.value as! [String: AnyObject]
        name = snapshotValue["name"] as! String
        addedByUser = snapshotValue["addedByUser"] as! String
        completed = snapshotValue["completed"] as! Bool
        ref = snapshot.ref
      }
      
      func toAnyObject() -> Any {
        return [
          "name": name,
          "addedByUser": addedByUser,
          "completed": completed
        ]
      }
      
    }
    

    And use toAnyObject() to save your item:

    let groceryItemRef = ref.child("items")
    
    groceryItemRef.setValue(groceryItem.toAnyObject())
    

    Source: https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2

    0 讨论(0)
提交回复
热议问题