I have a function that fetches a user from FireStore. At the moment the function manually deals with the data. I want to change this using Codable. I have tried many functions but I cannot seem to get it to work. I think the code that is required is quite simple and short & I'm just not understanding something.
Here is the fetchUser function that needs to change to one that doesn't manually map the data:
let ref = Firestore.firestore()
func fetchUser(uid: String,completion: @escaping (UserModel) -> ()){
let db = Firestore.firestore()
@EnvironmentObject var sharedData: SharedDataModel
ref.collection("Users").document(uid).getDocument { (doc, err) in
guard let user = doc else{return}
let username = user.data()?["username"] as? String ?? "No Username"
let pic = user.data()?["imageurl"] as? String ?? "No image URL"
let bio = user.data()?["bio"] as? String ?? "No bio"
let uid = user.data()?["uid"] as? String ?? ""
let isVerified = user.data()?["isVerified"] as? Bool ?? false
DispatchQueue.main.async {
completion(UserModel(username: username, pic: pic, bio: bio, uid: uid, isVerified: isVerified))
}
}
}
How do I change this function to manually map the data using Codable?
CodePudding user response:
try this approach, is this what you have tried already? Since I do not have your database, I cannot test this.
let ref = Firestore.firestore()
func fetchUser(uid: String, completion: @escaping (UserModel) -> ()){
let db = Firestore.firestore()
let docRef = db.collection("Users").document(uid).getDocument(as: UserModel.self) { result in
switch result {
case .success(let user):
// A `UserModel` value was successfully initialized from the DocumentSnapshot.
print("UserModel: \(user)")
DispatchQueue.main.async {
completion(user)
}
case .failure(let error):
// A `UserModel` value could not be initialized from the DocumentSnapshot.
print("Error decoding UserModel: \(error)")
// handle errors todo
}
}
}
CodePudding user response:
Add extinsion
extension DocumentSnapshot {
func toObject<T: Decodable>() throws -> T {
let jsonData = try JSONSerialization.data(withJSONObject: data()!, options: [])
let object = try JSONDecoder().decode(T.self, from: jsonData)
return object
}
}
confirm to decodable
struct UserModel:Decodable {
}
fetch user
func fetchUser(uid: String,completion: @escaping (UserModel?) -> ()){
let ref = Firestore.firestore()
ref.collection("Users").document(uid).getDocument() { (doc, err) in
guard err == nil else { completion(nil)
return
}
guard let model: UserModel = try? doc!.toObject() else {
completion(nil)
return
}
completion(model)
}
}
Second Way
user init with constructor as dict
struct UserModel {
let uid:String
let username:String
let pic:String
let bio:String
let isVerified:Bool
init?(value:[String:Any]) {
guard let id = value["uid"] as? String else {
return nil
}
uid = id
username = value["username"] as? String ?? "No Username"
pic = value["imageurl"] as? String ?? "No image URL"
bio = value["bio"] as? String ?? "No bio"
isVerified = value["isVerified"] as? Bool ?? false
}
}
and fetch
func fetchUser(uid: String,completion: @escaping (UserModel) -> ()){
let db = Firestore.firestore()
db.collection("Users").document(uid).getDocument { (doc, err) in
guard let documentSnap = doc,err == nil else {return}
if let value = documentSnap.data() ,
let user = UserModel(value: value) {
completion(user)
}
}
}