How to handle Firebase Auth Errors? / Swift / Firebase - swift

Hey guys can someone tell me how to handle this? I tried a lot but if I correct the one error another one is appearing...
Thanks in advance
Auth.auth().createUser(withEmail: eMailTextField.text!, password: passwordTextField.text!) { (data, error) in
if error != nil {
if let errCode = error as NSError? {
guard let errorCode = AuthErrorCode(rawValue: error) else {
print("there was an error logging in but it could not be matched with a firebase code")
return
}
switch errorCode {
case .FIRAuthErrorCodeNetworkError:
print("No Internet Connection")
case .ErrorCodeEmailAlreadyInUse:
print("in use")
default:
print("Create User Error: \(error!)")
}
}
} else {
print("all good... continue")
}

You can bridge to NSError and then create the AuthErrorCode based on error.code:
Auth.auth().createUser(withEmail: "MyEmail", password: "MyPassword") { authResult, error in
if error != nil, let error = error as NSError? {
if let errorCode = AuthErrorCode(rawValue: error.code) {
switch errorCode {
case .invalidEmail:
break
case .emailAlreadyInUse:
break
default:
break
}
}
} else {
//no error
}
}
Note that I only listed a couple of the errorCode possibilities - there's quite an extensive list of them.

Related

how to fix app freeze after dispatchsemaphore call

I was reading up on this question about app freezes and semaphores and I tried to implement the answer into my code, but it still freezes my app despite calling the UI work on the main thread. My goal is to stop the app from freezing once all the entries are called and have the UI work continue like normal.
This is the alert action I have in the deletion method so far:
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
let semaphore = DispatchSemaphore(value: 0)
self.deleteButton.isHidden = true
self.loadingToDelete.alpha = 1
self.loadingToDelete.startAnimating()
DispatchQueue.global(qos: .userInitiated).async {
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("The docs couldn't be retrieved for deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
print("The user being deleted has no events purchased.")
return
}
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.collection("student_users/\(user.uid)/events_bought/\(docID)/guests").getDocuments { (querySnap, error) in
guard querySnap?.isEmpty == false else {
print("The user being deleted has no guests with his purchases.")
return
}
for doc in querySnap!.documents {
let guest = doc.documentID
self.db.document("student_users/\(user.uid)/events_bought/\(docID)/guests/\(guest)").delete { (error) in
guard error == nil else {
print("Error deleting guests while deleting user.")
return
}
print("Guests deleted while deleting user!")
semaphore.signal()
}
semaphore.wait()
}
}
}
}
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("There was an error retrieving docs for user deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
return
}
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.document("student_users/\(user.uid)/events_bought/\(docID)").delete { (err) in
guard err == nil else {
print("There was an error deleting the the purchased events for the user being deleted.")
return
}
print("Purchases have been deleted for deleted user!")
semaphore.signal()
}
semaphore.wait()
}
}
self.db.document("student_users/\(user.uid)").delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting the user document.")
return
}
print("User doc deleted!")
semaphore.signal()
})
semaphore.wait()
user.delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting user from the system.")
return
}
print("User Deleted.")
semaphore.signal()
})
semaphore.wait()
DispatchQueue.main.async {
self.loadingToDelete.stopAnimating()
self.performSegue(withIdentifier: Constants.Segues.studentUserDeletedAccount, sender: self)
}
}
}
So this actually deletes everything cleanly with no residual data in the Firestore database, which is what I wanted to happen all along, the only issue is that the app freezes. I thought that the answer in the question I linked above would work in my case, but it didn't.
Also to mention, I've had suggestions of using Cloud Functions for this issue but my app has two types of users with different logic and syntax in the deletion process so I couldn't just use a simple auth().onDelete() in Cloud Functions and clean up residue. Even if I could, it would be the same issue I'm facing here but just on the server side, trying to order the tasks correctly, which in my opinion is repetitive and not the most sensible thing to do at this point.
Any other suggestions to overcome this issue? Thanks in advance.
EDIT Since semaphores are not the way to go, I resorted to this :
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
self.deleteButton.isHidden = true
self.loadingToDelete.alpha = 1
self.loadingToDelete.startAnimating()
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("The docs couldn't be retrieved for deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
print("The user being deleted has no events purchased.")
return
}
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.collection("student_users/\(user.uid)/events_bought/\(docID)/guests").getDocuments { (querySnap, error) in
guard querySnap?.isEmpty == false else {
print("The user being deleted has no guests with his purchases.")
return
}
let group = DispatchGroup()
for doc in querySnap!.documents {
let guest = doc.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(docID)/guests/\(guest)").delete { (error) in
guard error == nil else {
print("Error deleting guests while deleting user.")
return
}
print("Guests deleted while deleting user!")
group.leave()
}
}
}
}
}
self.db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("There was an error retrieving docs for user deletion.")
return
}
guard querySnapshot?.isEmpty == false else {
return
}
let group = DispatchGroup()
for document in querySnapshot!.documents {
let docID = document.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(docID)").delete { (err) in
guard err == nil else {
print("There was an error deleting the the purchased events for the user being deleted.")
return
}
print("Purchases have been deleted for deleted user!")
group.leave()
}
}
}
self.db.collection("student_users").whereField("userID", isEqualTo: user.uid).getDocuments { (querySnapshot, error) in
guard error == nil else {
print("There was an error deleting the user document.")
return
}
guard querySnapshot?.isEmpty == false else {
return
}
let group = DispatchGroup()
for document in querySnapshot!.documents {
let docID = document.documentID
group.enter()
self.db.document("student_users/\(docID)").delete { (err) in
guard err == nil else {
return
}
print("User doc deleted!")
group.leave()
}
}
}
let group = DispatchGroup()
group.enter()
user.delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting user from the system.")
return
}
print("User Deleted.")
group.leave()
})
group.notify(queue: .main) {
self.loadingToDelete.stopAnimating()
self.performSegue(withIdentifier: Constants.Segues.studentUserDeletedAccount, sender: self)
}
}
This still leaves residual data and does not execute the tasks in order. Any other suggestions?
Let me give you some ideas because I think your solution should incorporate some or all of these. First is how dispatch groups work and how you can nest them to execute blocks of async tasks in order:
func deleteUser(completion: #escaping (_ done: Bool) -> Void) {
// put UI into loading state
db.collection("someCollection").getDocuments { (snapshot, error) in
if let snapshot = snapshot {
if snapshot.isEmpty {
completion(true) // no errors, nothing to delete
} else {
let dispatchGroup = DispatchGroup() // instantiate the group outside the loop
var hasErrors = false
for doc in snapshot.documents {
dispatchGroup.enter() // enter on every iteration
db.document("someDocument").delete { (error) in
if let error = error {
print(error)
hasErrors = true
}
dispatchGroup.leave() // leave on every iteration regardless of outcome
}
}
dispatchGroup.notify(queue: .main) {
if hasErrors {
completion(false) // failed to delete
} else {
// execute next task and repeat
}
}
}
} else {
if let error = error {
print(error)
completion(false) // failed to delete
}
}
}
}
deleteUser { (done) in
if done {
// segue to next view controller
} else {
// retry or alert user
}
}
The example above is the basics of how dispatch group can work for you. When you leave the group the same number of times you've entered it, the completion handler is called. This example does not have any recursion and doesn't check if everything was actually deleted. Here is an example of how you could add some of that:
func deleteUser(completion: #escaping (_ done: Bool) -> Void) {
var retries = 0
func task() {
db.collection("someCollection").getDocuments { (snapshot, error) in
if let snapshot = snapshot {
if snapshot.isEmpty {
completion(true) // done, nothing left to delete
} else {
// delete the documents using a dispatch group or a Firestore batch delete
task() // call task again when this finishes
// because this function only exits when there is nothing left to delete
// or there have been too many failed attempts
}
} else {
if let error = error {
print(error)
}
retries += 1 // increment retries
run() // retry
}
}
}
func run() {
guard retries < 5 else {
completion(false) // 5 failed attempts, exit function
return
}
if retries == 0 {
task()
} else { // the more failures, the longer we wait until retrying
DispatchQueue.main.asyncAfter(deadline: .now() + Double(retries)) {
task()
}
}
}
run()
}
This doesn't answer your question directly but it should help you with the task overall. You can also forego some of the looping and deleting and do it all inside a Firestore batch operation, which comes with its own completion handler. There are lots of ways to tackle this but these are some things I'd consider.

How to have code inside of a guard else statement in swift?

I have the following code:
#IBAction func loginTapped(_ sender: Any) {
let error = validateFieldsSignIn()
if error != nil {
showErrorSignIn(error!)
} else {
guard let emailsignin = emailSignIn?.text!.trimmingCharacters(in: .whitespacesAndNewlines),
let passwordsignin = passwordSignIn?.text!.trimmingCharacters(in: .whitespacesAndNewlines) else {
return showErrorSignIn("Fill in all fields")
}
Auth.auth().signIn(withEmail: emailsignin, password: passwordsignin) { (user, error) in
if error != nil {
print("There was an error")
self.errorLabel3.text = "Invalid username or password"
self.errorLabel3.alpha = 1
} else {
self.transitionToHome()
}
}
}
}
Although unless the fields aren't filled in the else statement gets triggered and the error label says fill in all fields, essential the code that is getting triggered is this:
else {
return showErrorSignIn("Fill in all fields")
}
I tried putting the Auth.auth().signIn() inside the else block although I got the following error:
Variable declared in 'guard' condition is not usable in its body
How do I fix this error message?

update firebase database from current user

I want to store image in firebase storage and create url than update value from my current database in firebase, but it didn't store in firebase database from current user. this is my code, where I do wrong?
fileprivate func saveToFirebase(image: UIImage) {
guard let imageData = image.jpegData(compressionQuality: 0.3) else { return }
let uuidName = UUID().uuidString
Storage.storage().reference().child("profile_images").child(uuidName).putData(imageData, metadata: nil) { (metadata, error) in
if let err = error {
print("💥 -- Failed to upload images -- 💥")
print("💥 -- \(err) -- 💥")
return
}
metadata?.storageReference?.downloadURL(completion: { (url, error) in
if let err = error {
print("💥 -- Failed to create URL -- 💥")
print("💥 -- \(err) -- 💥")
return
}
guard let profileImageURL = url?.absoluteString else { return }
guard let currentUID = Auth.auth().currentUser?.uid else { return }
let userProfileURL = ["profileImageURL": profileImageURL]
Database.database().reference().child("users").child(currentUID).updateChildValues(userProfileURL, withCompletionBlock: { (error, reference) in
if let err = error {
print("💥 -- Failed to create URL -- 💥")
print("💥 -- \(err) -- 💥")
return
}
print("✅ -- Successfully update user data -- ✅")
})
})
}
}
Firstly we can create an enum to represent the different failures that could occur. Some scenarios we consider to be errors (such as a guard else returning void, or a broken chained optional) fail silently. Here we can encapsulate the different failure scenarios that need to be handled.
enum ProfileImageUploadError: Error {
case unauthenticatedUser
case invalidImageData
case failedDataUpload(Error?)
case failedDownloadURL(Error?)
case failedProfileUpdate(Error?)
var localizedDescription: String {
switch self {
case .failedDataUpload: return "Failed to upload data"
case .failedDownloadURL: return "Failed to download URL"
case .failedProfileUpdate: return "Failed to update profile"
default: return "\(self)"
}
}
var underlyingError: Error? {
switch self {
case .failedDataUpload(let err): return err
case .failedDownloadURL(let err): return err
case .failedProfileUpdate(let err): return err
default: return nil
}
}
}
Next, we can immediately determine that we have an authenticated user and that the image data checks out. When a guard fails, we call the completion block passing the error case for that scenario. Keeping this up, we construct our references to the storage and database providers, and attempt the sequence catching errors.
Given there is no error initially uploading the data, we can assume the image has been uploaded. Also rather than using the optional metadata, we can use the storage ref we constructed earlier to download the URL.
As we continue through the sequence of operations, we're trying to sufficiently handle what we consider to be the errors, until successful completion upon which point we can return the URL saved to Firebase Database.
func uploadProfileImage(_ image: UIImage, completion: #escaping (URL?, ProfileImageUploadError?) -> ()) {
guard let currentUser = Auth.auth().currentUser else {
return completion(nil, .unauthenticatedUser)
}
guard let imageData = image.jpegData(compressionQuality: 0.3) else {
return completion(nil, .invalidImageData)
}
let storagePath = "profile_images/\(UUID().uuidString)"
let databasePath = "users/\(currentUser.uid)/profileImageURL"
let profileImageDataRef = Storage.storage().reference(withPath: storagePath)
let profileImageURLRef = Database.database().reference(withPath: databasePath)
profileImageDataRef.putData(imageData, metadata: nil) { (metadata, error) in
guard error == nil else {
return completion(nil, .failedDataUpload(error))
}
profileImageDataRef.downloadURL { (url, error) in
guard let profileImageURL = url?.absoluteString else {
return completion(nil, .failedDownloadURL(error))
}
profileImageURLRef.setValue(profileImageURL, withCompletionBlock: { (error, ref) in
guard error == nil else {
return completion(nil, .failedProfileUpdate(error))
}
completion(url, nil)
})
}
}
}
Lastly, this is how you would use the function inside your existing one.
fileprivate func saveToFirebase(image: UIImage) {
uploadProfileImage(image) { (url, error) in
if let error = error {
print("💥 -- \(error.localizedDescription) -- 💥")
print("💥 -- \(error.underlyingError.debugDescription) -- 💥")
} else {
print("✅ -- Successfully update user data -- ✅")
print("✅ -- \(url.debugDescription) -- ✅")
}
}
}
This is not tested
So to recap, some of the lines in your function can 'fail' silently, this can be resolved by sufficiently handling the 'optionals' and errors with completions or simply print statements. I think it is the chain of optional properties leading up to the URL download causing the issue – specifically the metadata property in metadata?.storageReference?.downloadURL.
Try with this..
useruid is firbase uid
let storageRef = Storage.storage().reference().child("profile_images").child(useruid)
storageRef.putData(imagedata, metadata: nil, completion: { (metadatas, error) in
if error != nil {
print("Couldn't Upload Image")
} else {
print("Uploaded")
print(metadatas!)
storageRef.downloadURL(completion: { (url, error) in
if error != nil {
print(error!)
return
}
if url != nil {
let imageurl = String(describing: url!)
print(imageurl) }
})
}
})

Swift shows Expected declaration error while using firebase [duplicate]

This question already has answers here:
swift compiler shows Expected declaration error? [duplicate]
(3 answers)
Closed 4 years ago.
I'm new in Swift and I'm making a pretty simple login page using Firebase. I have this code:
guard fullNameTextField.text != "", emailTextField.text != "", passwordTextField.text != "", confirmPasswordTextField.text != ""
else {
return
}
}
if passwordTextField.text == confirmPasswordTextField.text {
//MARK: Firebase autenthication
FIRAuth.auth()?.createUser(withEmail: emailTextField.text!, password: passwordTextField.text!, completion:{
(user,error)in
if let error = error {
print(error.localizedDescription)
}
and the Xcode says that there is Expected a declaration error. I would be thankful if anyone could help me with this because I think I have tried everything.
I think you need to update to latest version
Auth.auth().createUser(withEmail: email, password: password) { (authResult, error) in
// ...
guard let user = authResult?.user else { return }
}
Have a look here Docs
You should avoid force unwrapping. Your code should look like this:
guard let name = fullNameTextField.text, name.count > 0 else { return }
guard let email = emailTextField.text, email.count > 0 else { return }
guard let password = passwordTextField.text, password.count > 0 else { return }
guard let confirmPassword = confirmPasswordTextField.text, confirmPassword.count > 0 else { return }
if password == confirmPassword {
Auth.auth().createUser(withEmail: email, password: password, completion: { (result, error) in
if let error = error {
print("Failed to create new user", error)
return
}
print("Successfully created user:", result?.user.uid ?? "")
})
}

Swift 2.0 Parse Login

So I've been attempting to update my code to Swift 2.0 syntax, but I can't seem to get my Parse login to work. I looked over the documentation changes and added the result block for my login, but I'm getting the error "'(, ) throws -> Void' is not convertible to 'PFUserResultBlock?'"
Here is the line of code:
PFUser.logInWithUsernameInBackground(usernameTextField.text!, password: passwordTextField.text!, block: { (user,error) -> Void in
if user != nil {
This Code might solve your problem.
PFUser.logInWithUsername(inBackground: emailTextField.text!, password: passwordTextField.text!, block: { (user, error) in
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
if error != nil {
var displayErrorMessage = "Please try again later"
let error = error as NSError?
if let errorMessage = error?.userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Login Error", message: displayErrorMessage)
} else {
print("logged inn")
self.performSegue(withIdentifier: "showUserTable", sender: self)
}
})
Try this version
PFUser.logInWithUsernameInBackground(usernameTextField.text!, password: passwordTextField.text!) { (user:PFUser?, error:NSError?) -> Void in
if user != nil {
print("Login Successful")
} else {
print("Login Failed")
}
}