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

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)
    }
})

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.


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