CodableFirebase icon indicating copy to clipboard operation
CodableFirebase copied to clipboard

Is it possible to decode the key?

Open jimijon opened this issue 6 years ago • 5 comments

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

jimijon avatar Mar 21 '18 23:03 jimijon

Hi @jimijon a similar issue was discussed in this link, I hope to help you.

denisoliveira avatar Mar 23 '18 21:03 denisoliveira

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

mongris avatar Mar 26 '18 22:03 mongris

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

VadimPavlov avatar Apr 10 '18 13:04 VadimPavlov

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.

phatmann avatar Apr 16 '18 17:04 phatmann

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
}

NickAtGit avatar Jan 24 '19 21:01 NickAtGit