SwiftUI - only one alert showing in nested if - swift

So I am creating a register view and I have two alerts to show. One is showing if the email or the password don't meet the requirements and the other one is used if the email is already saved in the system. The issue is that the single alert showing up is the one that checks if the email and password meet that set of requirements.
Button ("Sign Up") {
toggle.toggle()
if (isValidEmailAddr(strToValidate: userCredentials.email) && isValidPassword(strToVailidate: userCredentials.password)) {
if !emailDoesExist(emailToCheck: userCredentials.email) {
print("Email and Passowrd are valid")
saveJSON(email: userCredentials.email, password: userCredentials.password, nickname: userCredentials.nickname)
} else {
print("email address already in the system")
self.showAlert1.toggle()
}
}
else {
self.showAlert0.toggle()
}
}
.alert(isPresented: $showAlert1, content: { self.alert1 })
.alert(isPresented: $showAlert0, content: { self.alert0 })
.font(.title)
.buttonStyle(.bordered)
.frame(width:200, height: 100)

The problem is that you cannot attach more than one alert to the same view and now you have two for your button.
There are several ways to approach this to get only one alert and here I use a simple one where I use a second property that holds an error message that can be used in the alert
#State private var showAlert = false
#State private var errorMessage = ""
if (isValidEmailAddr(strToValidate: userCredentials.email) && isValidPassword(strToVailidate: userCredentials.password)) {
if !emailDoesExist(emailToCheck: userCredentials.email) {
saveJSON(email: userCredentials.email, password: userCredentials.password, nickname: userCredentials.nickname)
} else {
errorMessage = "Mail already exists"
self.showAlert.toggle()
}
} else {
errorMessage = "Mail or password is incorrect"
self.showAlert.toggle()
}
Then you would need to adjust the actual .alert modifier but since I don't know what self.alert0/1 are you need to do that yourself (most likely by keeping only one of them and make use of errorMessage).

Related

How can I display my user custom data in String format?

I'm new to Swift UI and MongoDB Realms. I'm trying to display a user's email (from their respective custom user data) in a text box but I'm getting a weird result that I don't know how to fix. I've been semi-blindly trying all sorts of things hoping something would work but no luck so far. I'm using Email/Password authorization so I know that the email/password wouldn't be stored in the user custom data (I think) but its just an example for what I'm trying to do.
My current code so far below.
struct HomeView: View {
let user = app.currentUser!
#State var isLoggingOut = false
var body: some View {
let userEmail = user.customData["email"] ?? "no email"
let userPassword = user.customData["password"] ?? "no password"
let userDisplay = user.customData["display"] ?? "no display"
let userName = user.customData["fullName"] ?? "no name"
ZStack {
Rectangle().foregroundColor(.yellow)
VStack {
Spacer()
Text("Home")
HStack {
Text("Email").frame(width: 100)
Spacer()
Text(String(reflecting: userEmail))
}.padding(.vertical)
HStack {
Text("Password").frame(width: 100)
Spacer()
Text(String(describing: userPassword))
}.padding(.vertical)
HStack {
Text("Display").frame(width: 100)
Spacer()
Text(String(describing: userDisplay))
}.padding(.vertical)
HStack {
Text("Full name").frame(width: 100)
Spacer()
Text(String(describing: userName))
}.padding(.vertical)
Spacer()
Button("Log Out") {tryLogOut()}
}.padding(40)
if isLoggingOut {LoginView()}
}
}
func tryLogOut() {
app.currentUser?.logOut {error in}
self.isLoggingOut = true
}
}
After logging in with a test user, this is what I'm getting in the right HStack text boxes (for example, the top email text box):
Email Optional(RealmSwift.AnyBSON.string("test123#gmail.com"))
Obviously what I'm trying to end up with is:
Email test123#gmail.com
What am I doing wrong? Everything else works as intended but this problem is giving me a headache. Any help would be appreciated.
Also FYI - Everything I am trying to display in the text boxes is stored in the database as Strings according to Atlas so I don't see the problem. However in my NewUserRegistrationView, when I create the new user document, I use the following code, I'm not sure if there is anything conflicting with the AnyBSON types before inserting the document.
struct NewUserRegistrationView: View {
// Email, password, displayName, and fullName obtained from TextFields in the body ...
// createUserDocument() is called after registering and confirming the user
func createUserDocument() {
let credentials = Credentials.emailPassword(
email: self.email,
password: self.password)
app.login(credentials: credentials) {result in
switch result {
case .failure:
self.statustext = "Document creation failed, try again"
case .success(let user):
let client = user.mongoClient("mongodb-atlas")
let database = client.database(named: "AppDatabase")
let collection = database.collection(withName: "userDocuments")
collection.insertOne([
"userID": AnyBSON(user.id),
"email": AnyBSON(self.email),
"password": AnyBSON(self.password),
"display": AnyBSON(self.display),
"fullName": AnyBSON(self.fullName)
]) { result in
switch result {
case .failure:
self.statustext = "Could not add document"
case .success(let newObjectId):
self.statustext = "Inserted document with objID: \(newObjectId)"
self.isDone = true
}
}
}
}
}
First of all conditionally downcast all dictionary values to AnyBSON to get the String value
let userEmail = (user.customData["email"] as? AnyBSON)?.stringValue ?? "no email"
let userPassword = (user.customData["password"] as? AnyBSON)?.stringValue ?? "no password"
let userDisplay = (user.customData["display"] as? AnyBSON)?.stringValue ?? "no display"
let userName = (user.customData["fullName"] as? AnyBSON)?.stringValue ?? "no name"
Without the cast you get Any which prints the weird output using String(reflecting
Then simply write
Text(userEmail)
...
Text(userPassword)
...
Text(userDisplay)
...
Text(userName)
It is because you are using String(...) change it to 'userEmail.description ?? ""`
The best way to display user data is to have a function in your class/struct that converts user data in whatever form to a string and then displays it. This would allow you to just use one function to convert data
I managed to figure it out but I have absolutely no idea why it works or what is happening. Lucky enough, I got it by brute force trial and error. Other answers didn't work unfortunately, but thanks to those for the suggestions.
I changed:
let userEmail = user.customData["email"] ?? "no email"
let userPassword = user.customData["password"] ?? "no password"
let userDisplay = user.customData["display"] ?? "no display"
let userName = user.customData["fullName"] ?? "no name"
To this:
let userEmail = ((user.customData["email"] ?? "email")?.stringValue)!
let userPassword = ((user.customData["password"] ?? "password")?.stringValue)!
let userDisplay = ((user.customData["display"] ?? "display")?.stringValue)!
let userName = ((user.customData["fullName"] ?? "fullName")?.stringValue)!
Text(userEmail)
Text(userPassword)
Text(userDisplay)
Text(userName)
Can anyone breakdown how this works? Like what do the question/exclamation marks do? And what is a simpler way to do this (if any)?
This solves the problem of decoding any AnyBSON. Not, user.customData...which in fact always returns nil!
I'm upset that it took so long to basically guess at the solution. And even this solution needs a refactor as I'm force unwrapping which means its a ticking bomb.
So I used the Realm call gist:
collection.findOneDocument(filter: ["userId": AnyBSON(user.id)])
Using the result:
case .success(let document):
let email = document?["email"] ?? "no email"
print("Email: \(String(describing: email!.stringValue!))")
So this returns the email string by itself. The fact I had to double-unwrap is insane.
Naturally, you want to guard and or if let as necessary. The document in this case is a Realm Document which is: Dictionary <String : AnyBSON?>

Open mail app with a specific mail in the inbox with Swift

I have the UID of a E-Mail message in the inbox.
Now I want to open Apple Mail (or alternatively Outlook) to present that mail to the user.
All examples I have found are how to start the standard Mail program to compose a new E-Mail.
Any Idea, hoe to solve that.
I tried with a Applescript but it does not work.
import SwiftUI
struct ContentView: View {
var body: some View {
Button(action: {
present_Email()
}) {
Text("Show E-Mail")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
func present_Email()
{
let myAppleScript = """
tell application "Mail"
set myMessages to messages 1 through 1 of inbox
repeat with aMesseage in myMessages
open (contents of aMesseage)
end repeat
end tell
"""
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
scriptObject.executeAndReturnError( &error)
}
}
Found a quite easy Solution:
To open the Mail App with a given Message-ID you can simply open it via the message: URL Scheme.
No Need for AppleScript ;-).
Button(action: {
NSWorkspace.shared.open(URL(string: "message:%3C...YOUR-MESSAGE_ID....%3E")!)
}) {
Text("Open E-Mail in Mail App")
}

how to handle an exception in change password?

Function which change password in my app:
#IBAction func changePassword(_ sender: Any) {
let isMatched = NSPredicate(format:"SELF MATCHES %#", regexAllValidation).evaluate(with: newPasswordField.text)
if (isMatched == true){
let auth = Auth()
auth.changePassword(oldPassword: oldPasswordField.text!,newPassword: newPasswordField.text!)
displayAlert(title: "Correct password", message: "Password changed!")
}
}
Class Auth:
func changePassword(oldPassword: String, newPassword: String){
let user =
AppDelegate.defaultUserPool().currentUser()?.changePassword(oldPassword, proposedPassword: newPassword)
AppDelegate.defaultUserPool().currentUser()?.clearSession()
}
When I call this function with bad old password, I receive in console:
Response body:
{"__type":"NotAuthorizedException","message":"Incorrect username or password."}
"x-amzn-errormessage" = "Incorrect username or password.";
"x-amzn-errortype" = "NotAuthorizedException:";
I would like to display an alert informing the user of a wrong password.
how do it?
Not sure which framework you are using, but if you are using swift and cognito, then I recommend the amplify framework, more specifically AWSMobileClient.
It is filled with common functionalities and heavily documented with common scenario like what you mentioned.
Link: https://aws-amplify.github.io/docs/sdk/ios/authentication
In short, you should have completion closure where you could handle the different scenarios and display alert if needed. For example, using AWSMobileClient you can
AWSMobileClient.default().confirmForgotPassword(username: "my_username", newPassword: "MyNewPassword123!!", confirmationCode: "ConfirmationCode") { (forgotPasswordResult, error) in
if let forgotPasswordResult = forgotPasswordResult {
switch(forgotPasswordResult.forgotPasswordState) {
case .done:
print("Password changed successfully")
default:
print("Error: Could not change password.")
}
} else if let error = error {
print("Error occurred: \(error.localizedDescription)")
}
}

Deleting PFUser objects from database in swift

I just started working with servers in swift and I'm using parse server to store a database of users when they create an account for my app. I have this function called sign up that is linked to a sign up button which works fine and properly stores user's info into my parse database.:
#IBAction func signUp(_ sender: AnyObject) {
//I first check to see if the users left any of the fields blank
if firstName.text == "" || lastName.text == "" || email.text == "" || userName.text == "" || password.text == "" {
createAlert(title: "Error in form", message: "Please fill in all text fields")
//If everything is filled in
}else{
let user = PFUser()
user.username = userName.text
user["firstname"] = firstName.text
user["lastname"] = lastName.text
user.email = email.text
user.password = password.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayErrorMessage = "Please try again later."
if let errorMessage = (error! as NSError).userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Signup error", message: displayErrorMessage)
}else{
print("User signed up")
}
})
}
}
Can anyone help me write a function that deletes a specified user or many loops through all users and deletes them.
Thanks
Don't put that code on a client. Yuck.
Deleting a user is something you want to safeguard very carefully. Put that in cloud code if you really need it, and do validation to ensure it's used properly.
I.e., a user should only be able to make the request to delete their own user. Make a cloud code function that has the parameter userId, and check that param against request.user.id. If they don't match, return an error. Otherwise you can call the destroy method on that User.
You should also put a CLP in place to only allow the master key to delete a User object.

Firebase How to verify an email to log in belongs to which category in database?

I am working on new Firebase Log in. There are two categories in database. "Cooks" and "Customer". "Cooks" has a CookViewController and "Customers" has a customerViewController. I need to know which category this email belongs to so after logging in, segue to CookViewController or CustomerViewController. Below is code for sign in, but I have no idea how to tell which category the email belongs to. Thanks!
#IBAction func loginPressed(sender: AnyObject) {
if let email = emailField.text where email != "", let password = passwordField.text where password != "" {
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (user, err) in
if err != nil {
print(err)
} else {
// Here I need to know whcih view Controller to Segue to:
self.performSegueWithIdentifier(identifier: String, sender: self)
}
}
Data Structure as below:
How about making your JSON like :-
yourApp:{
cooks:{
email1:true,
email3:true,
email4:true,
email7:true,
email8:true},
customers:{
email2:true,
email12:true,
email13:true,
email4:true,
email8:true},
users:{
uid1:{
email : blah#blah.com,
isCook: true
},
uid2:{
email : burp#blah.com,
isCook: false
}....
}
}
So you can either check in the cooks section or the customer section or the uid under users section itself and then relating to the isCook node, if he is ->Go to cookViewController, else go to customerViewController
Easiest way would be to check in the uid:-
FIRDatabase.database().reference().child("users/\(FIRAuth.auth()!.currentUser!.uid)/isCook").observeSingleEventOfType(.Value,withBlock:{(snap) in
if let isThisUserCook = snap.value as? Bool //or String{
if isThisUserCook == true{
//segue to cookVC
}else{
//segue to customerVC
}
}
})