CodableFirebase
CodableFirebase copied to clipboard
Is it possible to decode the key?
Ok I have a struct that I decode the object into. The object has a firestore generated ID. How can I get that generated ID to store it in my struct?
Thanks
Hi @jimijon a similar issue was discussed in this link, I hope to help you.
After read this link and hours investigation, I think you should set auto generated ID manually.
struct Model: Codable {
enum CodingKeys: String, CodingKey {
case name
}
var id: String? // for auto generated ID
let name: String
init(from: decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
}
Decode:
var model = FirebaseDecoder().decode(Model.self, from: snapshot.value)
model.id = snapshot.key // set auto generated ID manually
I prefer to define
protocol FirestoreDecodable: Decodable {
var documentID: String { get }
}
struct MyObject: FirestoreDecodable {
let documentID: String // for auto generated ID
let name: String
}
So on decoding side it looks like:
let decoder = FirestoreDecoder()
var data = document.data() ?? [:]
data["documentID"] = document.documentID
let object = try decoder.decode(Object.self, from: data)
where Object is FirestoreDecodable Same can be done for Firebase
Here is what I ended up doing:
protocol FirestoreCodable: Codable {
mutating func setDocumentId(_ documentId: String)
}
extension FirestoreCodable {
func setDocumentId(_ documentId: String) {}
}
struct MyObject: FirestoreCodable {
var id: String!
var otherProp: String
mutating func setDocumentId(_ documentId: String) {
id = documentId
}
}
func decode<T: FirestoreCodable>(from snapshot: DocumentSnapshot) -> T? {
if let data = snapshot.data() {
do {
let object = try FirestoreDecoder().decode(T.self, from: data)
object.setDocumentId(snapshot.documentID)
return object
} catch {}
}
return nil
}
Then any object that is interested in grabbing documentId overrides setDocumentId
. Works very well.
I adapted @phatmann 's very nice solution for firebase real time database, which I think had an error 🤔 It did work for me when I added the mutating
keyword in the FirebaseCodable extension. I also constrained the protocol a little more so that the id
property is enforced
protocol FirebaseCodable: Codable {
var id: String! {get set}
mutating func setId(_ documentId: String)
}
extension FirebaseCodable {
mutating func setId(_ id: String) {}
}
func decode<T: FirebaseCodable>(from snapshot: DataSnapshot) -> T? {
if let data = snapshot.value {
do {
var object = try FirebaseDecoder().decode(T.self, from: data)
object.setId(snapshot.key)
return object
} catch let error {
print(error.localizedDescription)
}
}
return nil
}