Reputation: 3
I want to get document id to work with it for editing and deleting document after decoding. How can I do this?
My model:
struct MoodRecord: Codable, Hashable, Identifiable {
@DocumentID var id: String?
let user: String
let date: String
let time: String
let mood: Int
}
My function:
class func getRecords <T: Decodable> (
reference: CollectionReference,
type: T.Type,
completion: @escaping (Result<[T], Error>) -> Void
) {
reference.whereField("user", isEqualTo: AuthManager.shared.getUserId() ?? "")
.getDocuments { snapshot, error in
if let documents = snapshot?.documents {
do {
let records: [T] = try documents.map { try $0.decoded(type: T.self) }
completion(.success(records))
} catch let error {
completion(.failure(error))
}
} else if let error = error {
completion(.failure(error))
}
}
}
My decoder:
extension QuerySnapshot {
func decoded <T: Decodable> (type: T.Type) throws -> [T] {
let objects: [T] = try documents.map { try $0.decoded(type: T.self) }
return objects
}
}
extension QueryDocumentSnapshot {
func decoded <T: Decodable> (type: T.Type) throws -> T {
let jsonData = try JSONSerialization.data(withJSONObject: data(), options: [])
let object = try JSONDecoder().decode(type.self, from: jsonData)
return object
}
}
I use only auto-ID in Firestore and want to work with them in this task. Can I do this?
Upvotes: 0
Views: 1012
Reputation: 7254
You can use Firestore's Codable support to map document IDs. No need to implement a custom decoder - we've done all the hard work for you.
Here is how.
You already did this. Looking at the attributes in your MoodRecord
struct, I assume you want to use date
and time
to track timestamps, and mood
to capture the value of an enum. I've updated the struct accordingly:
struct MoodRecord: Codable, Hashable, Identifiable {
@DocumentID var id: String?
var user: String
var date: Date
var time: Date
var mood: Mood
}
enum Mood: String, Codable {
case great
case ok
case good
case bad
case terrible
}
Fetching Firestore documents and mapping them to Swift structs becomes a one-liner thanks to Codable:
docRef.getDocument(as: MoodRecord.self) { result in
// handle result
}
Here is a complete code snippet for fetching a single document:
private func fetchMoodRecord(documentId: String) {
let docRef = db.collection("moodrecords").document(documentId)
docRef.getDocument(as: MoodRecord.self) { result in
switch result {
case .success(let moodRecord):
// A MoodRecord value was successfully initialized from the DocumentSnapshot.
self.moodRecord = moodRecord
self.errorMessage = nil
case .failure(let error):
// A MoodRecord value could not be initialized from the DocumentSnapshot.
self.errorMessage = "Error decoding document: \(error.localizedDescription)"
}
}
}
To update a document using Codable, use the following code snippet:
func updateMoodRecord(moodRecord: MoodRecord) {
if let id = moodRecord.id {
let docRef = db.collection("moodRecords").document(id)
do {
try docRef.setData(from: moodRecord)
}
catch {
print(error)
}
}
}
Adding new documents is even easier:
func addMoodRecord(moodRecord: MoodRecord) {
let collectionRef = db.collection("moodRecords")
do {
let newDocReference = try collectionRef.addDocument(from: moodRecord)
print("Mood record stored with new document reference: \(newDocReference)")
}
catch {
print(error)
}
}
To learn more about how to map Firestore documents using Swift's Codable protocol, including how to map advanced data types such as date, time, colors, enums, how to fetch data using snapshot listeners, and how to handle any errors that might occur during the mapping process, check out Mapping Firestore Data in Swift - The Comprehensive Guide and the accompanying sample project on GitHub
Upvotes: 3