Home > Software engineering >  How to add an unescaped closure Swift Firebase
How to add an unescaped closure Swift Firebase

Time:02-14

When I compile I get this error "The path to the document cannot be empty".

To fix this, I should add an unescaped closure. How to add an unescaped closure to the fetchUsers () function and call the GetCorsodiLaurea () function in the closure? In such a way, the compiler will not try to execute functions asynchronously.

LoginViewModel

import SwiftUI
import Firebase
import LocalAuthentication

class LoginViewModel: ObservableObject {
@Published var email: String = ""
@Published var password: String = ""


@AppStorage("use_face_id") var useFaceID: Bool = false
@AppStorage("use_face_email") var faceIDEmail: String = ""
@AppStorage("use_face_password") var faceIDPassword: String = ""

//Log Status

@AppStorage("log_status") var logStatus: Bool = false

//MARK: Error
@Published var showError: Bool = false
@Published var errorMsg: String = ""

// MARK: Firebase Login
func loginUser(useFaceID: Bool,email: String = "",password: String = "")async throws{
    
    let _ = try await Auth.auth().signIn(withEmail: email != "" ? email : self.email, password: password != "" ? password : self.password)
    DispatchQueue.main.async {
        if useFaceID && self.faceIDEmail == ""{
            self.useFaceID = useFaceID
            // MARK: Storing for future face ID Login
            self.faceIDEmail = self.email
            self.faceIDPassword = self.password
        }
        
        self.logStatus = true
        
    }
    }
    

//MARK: FaceID Usage
func getBioMetricStatus()->Bool{
    
    let scanner = LAContext()
    
    return scanner.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: .none)
    
}

// MARK: FaceID Login
func autenticationUser()async throws{
    
    let status = try await LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "To Login Into App")
    
    if status{
        try await loginUser(useFaceID: useFaceID,email: self.faceIDEmail,password: self.faceIDPassword)
    }
    
}

}

ProfileViewModel

import Firebase
import FirebaseDatabase
import FirebaseFirestoreSwift
import SwiftUI





class ProfileViewModel: ObservableObject {
@Published var userInfo: UserModel = .empty
@Published var userDegree: userDegreeModel = .empty
@Published var isSignedIn = false
@Published var showError: Bool = false
@Published var errorMsg: String = ""

var uid = Auth.auth().currentUser!.uid
let db = Firestore.firestore()


 init() {
// listen for auth state change and set isSignedIn property accordingly
Auth.auth().addStateDidChangeListener { auth, user in
  if let user = user {
    print("Signed in as user \(user.uid).")
    self.uid = user.uid
    self.isSignedIn = true
  }
  else {
    self.isSignedIn = false
    self.userInfo.Nomeintero = ""
  }
}

  fetchUser() { [self] in
    fetchDegrees()
}
}

func fetchUser(completion: @escaping () -> Void) {
let docRef = db.collection("users").document(uid)

docRef.getDocument { document, error in
  if let error = error as NSError? {
    self.errorMsg = "Error getting document: \(error.localizedDescription)"
  }
  else {
    if let document = document {
      do {
          self.userInfo = try document.data(as: UserModel.self)!
          completion()
      }
      catch {
        print(error)
      }
    }
  }
}
}

func fetchDegrees() {
  let docRef = db.collection("DegreeCourses").document(userInfo.Tipocorso)

docRef.getDocument { document, error in
  if let error = error as NSError? {
    self.errorMsg = "Error getting document: \(error.localizedDescription)"
  }
  else {
    if let document = document {
      do {
          self.userDegree = try document.data(as: userDegreeModel.self)!
          
      }
      catch {
        print(error)
      }
    }
  }
}
}

}

UserModel

import SwiftUI
import Firebase
import FirebaseDatabase
import FirebaseFirestoreSwift


public struct UserModel: Codable{
@DocumentID var id: String?
var Nome : String
var Cognome : String
var photoURL : String
var Nomeintero : String
var Corsodilaurea : String
var Tipocorso : String
 }

extension UserModel {
static let empty = UserModel(Nome: "", Cognome: "", photoURL: "", Nomeintero: "", Corsodilaurea: "", Tipocorso: "")
}

userDegreeModel

import SwiftUI
import Firebase
import FirebaseDatabase
import FirebaseFirestoreSwift

struct userDegreeModel: Codable {
@DocumentID var id: String?
var Name : String
var TotalSubjects : Int
}


extension userDegreeModel {
static let empty = userDegreeModel(Name: "", TotalSubjects: 0)
}

Error

CodePudding user response:

A couple of notes:

  1. Firestore calls return on the main dispatch queue already about this), so you don't need to manually switch to the main queue using DispatchQueue.async { }. See my Twitter thread for more details.
  2. Instead of mapping Firestore documents manually, you can use Codable to do so. This means less code to write, and fewer typos :-) Here is an article that goes into much more detail: Mapping Firestore Data in Swift - The Comprehensive Guide | Peter Friese
  3. Accessing the signed in user using Auth.auth().currentUser!.uid might result in you app breaking if no user is signed in. I recommend implementing an authentication state listener instead.
  4. Since all of Firebase's APIs are asynchronous (see my blog post about this: Calling asynchronous Firebase APIs from Swift - Callbacks, Combine, and async/await | Peter Friese), the result of fetchUser will take a short moment, so you want to make sure to only call fetchDegrees once that call has completed. One way to do this is to use a completion handler.

Lastly, I recommend following a styleguide like this one for naming your classes and attribute: Swift Style Guide

I've updated your code accordingly below.

import Firebase
import FirebaseDatabase
import FirebaseFirestoreSwift
import SwiftUI

public struct UserModel: Codable {
  @DocumentID var id: String?
  var firstName: String
  var lastName: String
  var photoUrl: String
  // ...
}

extension UserModel {
  static let empty = UserModel(firstName: "", lastName: "", photoUrl: "")
}

public struct UserDegreeModel: Codable {
  @DocumentID var id: String?
  var name: String
  var totalSubjects: Int
  // ...
}

extension UserDegreeModel {
  static let empty = UserDegreeModel(name: "", totalSubjects: 0)
}


class ProfileViewModel: ObservableObject {
  @Published var userInfo: UserModel = .empty
  @Published var userDegree: UserDegreeModel = .empty
  @Published var isSignedIn = false

  let uid = Auth.auth().currentUser!.uid
  let db = Firestore.firestore()

  init() {
    // listen for auth state change and set isSignedIn property accordingly
    Auth.auth().addStateDidChangeListener { auth, user in
      if let user = user {
        print("Signed in as user \(user.uid).")
        self.uid = user.uid
        self.isSignedIn = true
      }
      else {
        self.isSignedIn = false
        self.username = ""
      }
    }

    fetchUser() { 
      fetchDegrees()
    }
  }

  func fetchUser(completion: () -> Void) {
    let docRef = db.collection("users").document(uid)

    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: UserModel.self)
            completion()
          }
          catch {
            print(error)
          }
        }
      }
    }
  }

  func fetchDegrees() {
    let docRef = db.collection("DegreeCourses").document(userInfo.Tipocorso)

    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.userDegree = try document.data(as: UserDegreeModel.self)
          }
          catch {
            print(error)
          }
        }
      }
    }
  }

}
  • Related