Swift and Firebase: Value of type User has no member asDict()

Viewed 63

I am new to Swift and Firebase. I'm trying to write a code that will convert user struct into dictionary form, that will be saved to Firebase Database. I wanted to try converting UserClass to the dictionary form with JSONEncoder, although I get the error "Value of type User has no member asDict()". Here's the code of the program.

Here's the module that is storage service for Firebase:

import Foundation
import Firebase
import FirebaseAuth
import FirebaseStorage


class StorageService {
    
    static var storage = Storage.storage()
    
    static var storageRoot = storage.reference(forURL: "link to firebase")
    
    static var storageProfile = storageRoot.child("profile")
    
    static func storageProfileId(userId: String) -> StorageReference{
        return storageProfile.child(userId)
    }
    
    static func saveProfileImage(userId: String, username: String, email: String, imageData: Data, metaData:StorageMetadata, storageProfileImageRef:StorageReference, onSuccess: @escaping(_ user: UserClass) -> Void, onError: @escaping(_ errorMessage:String) -> Void){
        
        storageProfileImageRef.putData(imageData, metadata: metaData){
            (StorageMetadata, error) in
            
            if error != nil{
                onError(error!.localizedDescription)
            }
            
            storageProfileImageRef.downloadURL{
                (url, error) in
                
                if let metaImageUrl = url?.absoluteString {
                    if let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest() {
                        changeRequest.photoURL = url
                        changeRequest.displayName = username
                        changeRequest.commitChanges {
                            (error) in
                            if error != nil {
                                onError(error!.localizedDescription)
                                return
                            }
                        }
                    }
                    let firestoreUserId = AuthService.getUserId(userId: userId)

                    let user = UserClass.init(uid: userId, email: email, profileImageUrl: metaImageUrl, username: username, searchName: username.splitString(), description: "")

                    
                    guard let dict: [String: Any] = try?user.asDict() else {return}
                    
                    firestoreUserId.setData(dict){
                        (error) in
                        if error != nil {
                            onError(error!.localizedDescription)
                            return
                        }
                    }
                    onSuccess(user)
            }
        }
    
        }
    }
}

Here's user struct:

struct UserClass {
    var uid:String
    var email:String
    var profileImageUrl:String
    var username:String
    var searchName:[String]
    var description:String
}

And here's extension of JSON Encoder

extension Encodable{
    func asDict() throws -> [String: Any] {
        let data = try JSONEncoder().encode(self)
        guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
        else{
           throw NSError()
        }
        return dictionary
    }
}

It's probably a small error, that I haven't seen. I'm trying to find solution for it for days and no luck. I will appreciate any kind of help from you fellow and more experienced programmers.

2 Answers

Maybe you forgot conforming to Encodable

struct UserClass: Encodable {
    var uid:String
    var email:String
    var profileImageUrl:String
    var username:String
    var searchName:[String]
    var description:String
}

Please do not map data manually when using Firestore - this is an antipattern. Here is a comprehensive article that shows in detail how to

  • model your data
  • map your data in a type-safe way

In short:

struct User: Codable, Identifiable {
  var id: String?
  var email: String
  var profileImageUrl: String
  var username: String
  var searchName: [String]
  var description: String
}
func fetchUser(documentId: String) {
  let docRef = db.collection("users").document(documentId)
  docRef.getDocument { document, error in
    if let error = error as NSError? {
      self.errorMessage = "Error getting document: \(error.localizedDescription)"
    }
    else {
      if let document = document {
        do {
          self.user = try document.data(as: User.self) 
        }
        catch {
          print(error)
        }
      }
    }
  }
}

func addUser() {
    let collectionRef = db.collection("users")
    do {
      let newDocReference = try collectionRef.addDocument(from: self.user)
      print("User stored with new document reference: \(newDocReference)")
    }
    catch {
      print(error)
    }
  }
Related