linking of If Else Statements with picker - swift

Beginner coder here. I have a question with Xcode. I am developing a horoscope app and have a question on linking my If Else Statements with picker input values.
The logic in the app currently is, User selects date listed, hits button, and an alert message pops up containing specific copy related to their input. How would I get my if else statements to print in the alert message?
TLDR; Need help with linking function print value with alert window message
Here is a sample of how it currently looks, I am also getting an error of [Cannot convert value of type '()' to expected argument type 'String?'] in line 3 here. Thank you in advance!
let message = signselection()
let alert = UIAlertController (title: "Your Astrological Sign is...", message:message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) {(action) -> Void in}
func signselection () {
if (curMonth == 0) {
if(curDay! < 20) {
print("Capricorn Message")
} else {
print("Aquarius Message")
}
}
if (curMonth == 1){
if(curDay! < 19) {
print ("Aquarius Message")
} else {
print ("Pisces Message")
} }
if (curMonth == 2){
if(curDay! < 20) {
print ("Pisces Message")
} else {
print ("Aries Message")
} }

The signselection() method only prints the message, but it should return a string. That's why you're getting the error saying that it can't cast void, "()", to String. The function declaration should be:
func signSelection() -> String
and in stead of print("Message") you should
return "Message" //where "Message" is each message you're currently printing
You should also add the okAction to there alert with:
alert.addAction(okAction)

Related

How to get auth error from sign in/sign up AWS Amplify?

I am currently trying to get what type of error is thrown when a user attempts to sign in/ registers but my switch case is not working because I don't know what enum I am supposed to compare to. Once the error can be determined the app displays a UIAlertController explaining the error. If I print(err.code) I just get an Int back. Can anyone steer me in the right direction? I couldn't find any docs on how to handle it.
func signIn(username: String, password: String) {
Amplify.Auth.signIn(username: username, password: password) { result in
switch result {
case .success:
print("Sign in succeeded")
//Go to root vc
case .failure(let error):
print("Sign in failed \(error)")
if let err = error as NSError?{
switch err.code {
case AWSCognitoIdentityProviderErrorType.unknown.rawValue:
self.presentAlert(errorTitle: "Unkown Error", errorMessage: "An unknown error has occured", buttonText: "Ok")
print("Unkown error")
case AWSCognitoIdentityProviderErrorType.invalidPassword.rawValue:
self.presentAlert(errorTitle: "Invalid Password", errorMessage: "You have entered an invalid password", buttonText: "Try Again")
case AWSCognitoIdentityProviderErrorType.tooManyFailedAttempts.rawValue:
self.presentAlert(errorTitle: "Excedded login trys", errorMessage: "You attempted to login too many times", buttonText: "Try Again Later")
case AWSCognitoIdentityProviderErrorType.userNotFound.rawValue:
self.presentAlert(errorTitle: "Unknown Credentials", errorMessage: "No user exists with the credentials you entered.", buttonText: "Try Again")
default:
break
}
}
}
}
}
The type of error thrown by the Amplify Authentication library is an AuthError type, which contains a more helpful embedded AWSCognitoAuthError type. The AWS documentation is confusing and misleading because AWS provides two SDKs at this time: the AWS Mobile SDK and the newer, dumber Amplify Libraries.
It looks like you are using the Amplify Libraries. If you break your code in the Xcode debugger on the .failure case, the debugger variable display shows error as Amplify.AuthError which requires a PhD in AWS to make sense of. But for purposes of illumination, if you slug this in under the .failure case
case .failure(let error):
if let actualError = error.underlyingError as NSError? {
print("Cast to nserror:", actualError)
}
you will get Cast to nserror: AWSCognitoAuthPlugin.AWSCognitoAuthError.xxx where the xxx might be usernameExists or some other useful error. It turns out AWSCognitoAuthError is a straightforward enum which you can peruse by typing AWSCognitoAuthError at some random spot in your source, then right-clicking and choosing Jump to Definition in the Xcode popup menu.
I prefer to keep all the voodoo Amplify knowledge isolated, so I created a User class to interact with AWS Authentication services. I include the error handling portions here in hopes it saves you some grief.
// At the top of the file, outside the class:
import Amplify
import AWSCognitoAuthPlugin
import AWSPluginsCore
import Foundation
// This typealias allows other modules to interpret error responses
// from the AWSCognitoAuthError enum, such as ".usernameExists" without
// needing to import Amplify everywhere.
typealias AWSError = AWSCognitoAuthError
// Amplify Auth methods return AuthError type errors, which are complex multilayer
// enums that require complex deciphering if you want to extract a simple error
// to guide the user.
//
// This custom Error type allows callers from other modules to receive a simple
// explanatory "message" string, which is pulled from the .errorDescription
// property of an Amplify AuthError. The "error" optional is the returned
// .underlyingError property cast to an AWSCognitoAuthError type, which is an
// enum of all the possible problems interacting with Amplify Auth such as
// .usernameExists, .userNotConfirmed, .codeMismatch, or .codeExpired.
struct UserError: Error {
let message: String
let error: AWSError?
init(message: String?, error: Error?) {
self.message = message ?? "Unknown error"
self.error = error as? AWSError
}
}
// Example of usage, within the class. In my case the class signIn()
// function includes an onCompletion callback parameter to allow the
// caller to inform the user and change the UI according to the results.
func signIn(username: String, password: String,
onCompletion: #escaping (Result<Bool, UserError>) -> Void) {
_ = Amplify.Auth.signIn(username: username, password: password) { result in
switch result {
case .success(let signInResult):
onCompletion(.success(signInResult.isSignedIn))
case .failure(let authError):
let awsError = authError.underlyingError as? AWSError ?? AWSError.userNotFound
let userError = UserError(message: authError.errorDescription,
error: awsError)
onCompletion(.failure(userError))
}
}
}
In your view controller, here is how you might process the response from the example signIn() User class method above:
// Inside e.g. an #IBAction function
User.sharedUser.signIn(email, password: password, onCompletion: { result in
switch result {
case .success:
// Go to root View Controller
case .failure(let userError):
if let authError = userError.error {
if (authError == .userNotConfirmed) {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ShowConfirm", sender: nil)
}
return
}
}
let alert = UIAlertController(title: userError.message, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
DispatchQueue.main.async {
self.present(alert, animated: true, completion: nil)
}
}
})

How to catch error when Firestore collection already exists?

Right now my app is crashing when a collection already exists in Firebase Firestore. I want to catch this error when it happens, but my current implementation doesn't catch anything as the addSnapshotListener() method does not throw any error.
Current Code
let db = Firestore.firestore()
do {
try db.collection(chatName).addSnapshotListener { (Query, Error) in
if Error != nil || Query?.documents != nil {
let alert = UIAlertController(title: "Chat Name Already Exists", message: "This chat name already exists, try again with a different name", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: .default, handler: { (UIAlertAction) in
alert.dismiss(animated: true, completion: nil)}))
AllChatsViewController().present(alert, animated: true)
completion()
}
else {
self.addChatToProfile(withName: chatName) {
completion()
}
}
}
}
catch {
let alert = UIAlertController(title: "Chat Name Already Exists", message: "This chat name already exists, try again with a different name", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: .default, handler: { (UIAlertAction) in
alert.dismiss(animated: true, completion: nil)}))
AllChatsViewController().present(alert, animated: true)
completion()
}
Error After App Crashes
Thread 1: "Invalid collection reference. Collection references must have an odd number of segments, but has 0"
How can I catch this error so I can display an UIAlertController with the error?
I would use a different approach.
To test if a collection exists, read that collection by name and determine if there are any documents via snapshot.count equals 0
The gotcha here is that a collection could have a large amount of data and there's no reason to read all of that in or attach a listener so we need to use a known field within that collection to limit the results.
I would suggest a function with a closure that returns true if the collection exists, false if not and then take action based on that result.
You'll need the name of the collection you want to test and then the name of a known field within that collection to query for to limit the results.
The field name is important in that if the collection has 1M documents, you don't want to read them all in - you just want to read one and .orderBy with a limit will do that.
So here's a calling function
func checkCollection() {
self.doesCollectionExist(collectionName: "test_collection", fieldName: "msg", completion: { isEmpty in
if isEmpty == true {
print("collection does not exist")
} else {
print("collection found!")
}
})
}
and then the function that checks to see if the collection exists by reading one document and returns false if not, true if it does.
func doesCollectionExist(collectionName: String, fieldName: String, completion: #escaping ( (Bool) -> Void ) ) {
let ref = self.db.collection(collectionName)
let query = ref.order(by: fieldName).limit(toLast: 1)
query.getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
if snapshot!.count == 0 {
completion(true)
} else {
completion(false)
}
})
}
That error doesn't have anything to do with a collection not existing The error suggests that chatName is an empty string, which is an invalid parameter. Instead of catching an error, you should instead first validate that chatName is a valid collection name string before sending it to the Firestore API.
If you query a collection that doesn't exist, you won't get that error message at all. Instead, you will simply get no documents in the result set.
you will make something like this:
Firestore.firestore().collection(chatName).addSnapshotListener { (query, error) in
if let error = error {
//in this part you have the error, do something like present alert with error or something you want
print(error)
}
// in this part the success

Function not stopping after handleComplete

I created a basic Google Places app that lets users check-in to a location. When a user tries to check in, I loop through the list of likelihood places to verify that the user is actually at the location in the app. However, when I try to escape the loop after confirming the location is correct, my function still ends up going to my "else" situation (an error message that asks the user to please check in to the correct location).
The following function gets called in viewWillAppear:
func checkIn(handleComplete:#escaping (()->())){
guard let currentUserID = User.current?.key else {return}
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue))!
placesClient.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: fields, callback: {
(placeLikelihoodList: Array<GMSPlaceLikelihood>?, error: Error?) in
if let error = error {
print("An error occurred: \(error.localizedDescription)")
return
}
if let placeLikelihoodList = placeLikelihoodList {
for likelihood in placeLikelihoodList {
let place = likelihood.place
if likelihood.likelihood >= 0.75 && place.placeID! == self.hangoutID {
let place = likelihood.place
print("Current Place name \(String(describing: place.name!)) at likelihood \(likelihood.likelihood)")
print("Current PlaceID \(String(describing: place.placeID!))")
self.delta = 0.0
// update checkin
DispatchQueue.main.async {
let hangoutRef = self.db.collection("users").document(currentUserID).collection("hangout").document(self.hangoutID).updateData([
"lastCheckin": Date()
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
handleComplete()
}
}
self.presentDismissableAlert(title: "", message: "Please check in to the hangout to join this chat", button: "OK", dismissed: { (UIAlertAction) in
self.performSegue(withIdentifier: "unwindSegueToChats", sender: self)
})
}
})
}
If the correct conditions are met, the code will land on the handleComplete() line but then it will still execute the dismissableAlert underneath and segue the user out of the room. How can I fix the flow so that the app will cycle through the list of likely Places and stop the function on handleComplete if the correct condition is met, or else then proceed to the error message if the correct conditions are not met (user is not at the correct Place)?
Thanks

Displaying an alert in Swift

I'm learning swift and I'm building a UI for an app, I wanted to know what was wrong with my code because it keeps saying I have errors.
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userRepeatPassword = repeatPasswordTextField.text;
// check for empty fields
if((userEmail.isEmpty || userPassword.isEmpty || userRepeatPassword.isEmpty)
{
//Display alert message
displayMyAlertMessage("Alert fields are required");
return;
}
//check if passwords match
if(userPassword != userRepeatPassword)
// error expected expression in list of expression
{
//Display alert message
displayMyAlertMessage("Alert fields are required");
return;
}
expected '(" after if condition
func displayMyAlertMessage(userMessage: String)
{
var myAlert = UIAlertController(
title: "Alert",
message: userMessage,
PrefferedStyle:UIAlertController.Alert);
}
error Type"UIAlertController'has no member 'Alert'
Just need help figuring out the errors for the code
This code line of yours:
var myAlert = UIAlertController(title:"Alert", message:
userMessage, PrefferedStyle:UIAlertController.Alert);
should be:
var myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: .Alert);
Alert is a case of the enum UIAlertControllerStyle, so it should be UIAlertControllerStyle.Alert instead of UIAlertController.Alert. Additionally, because the compiler know what type of enum the parameter "preferredStyle" expect, so you can use .Alert instead.
There are more '(' than ')' in your first if condition:
if((userEmail.isEmpty || userPassword.isEmpty ||
userRepeatPassword.isEmpty) {
thus causing the first error. You should change it to:
if(userEmail.isEmpty || userPassword.isEmpty ||
userRepeatPassword.isEmpty) {
And to follow Swift coding convention, you should finally change it to:
if userEmail.isEmpty || userPassword.isEmpty ||
userRepeatPassword.isEmpty {
let userEmail = userEmailTextField.text
let userPassword = userPasswordTextField.text
let userRepeatPassword = repeatPasswordTextField.text
// check for empty fields
if userEmail.isEmpty || userPassword.isEmpty ||
userRepeatPassword.isEmpty {
//Display alert message
displayMyAlertMessage("Alert fields are required")
return;
}
//check if passwords match
if userPassword != userRepeatPassword
//error expected expression in list of expression
{
//Display alert message
displayMyAlertMessage("Alert fields are required")
return
}
You do not need () after if statements in swift. Swift might consider them as tuples. You can just do
if condition {
}
Also you do not need semicolon after each statement in swift. You can remove ;
#Dat Hoang answer is correct but your alert actually won't show up until you present it. Add this line towards the end of your displayMyAlertMessage method
present(self, animated: true, completion: nil)
I'd recommend you check out this Swift Coding Styling Guide as well

optional type 'Bool' cannot be used as a boolean; Test for '!=nil' instead

optional type 'Bool' cannot be used as a boolean; Test for '!=nil' instead
I got an error at first if, by replacing the if condition (after), the second if condition did never run. Any idea?
Before:
if(userEmail?.isEmpty || userPassword?.isEmpty || userRepeatPassword?.isEmpty){
displayMyAlertMessage("All fields are required")
return
}
if(userPassword != userRepeatPassword){
displayMyAlertMessage("Passwords do not match.")
}
After:
if(userEmail != nil || userPassword != nil || userRepeatPassword != nil){
displayMyAlertMessage("All fields are required")
return
}
if(userPassword != userRepeatPassword){
displayMyAlertMessage("Passwords do not match.")
}
you need to wrapped it with ! instead of ?.
That will solved the error message:
if (username!.isEmpty) .....
if(userEmail!.isEmpty || userPassword!.isEmpty || userRepeatPassword!.isEmpty)
You can't check the value of an optional since , you probably already know, being an optional implies it can or cannot be there. One solution is called force unwrapping and it is being done by using an "!" ( exclamation mark ). "?" ( question mark ) just lets the compiler know there may be or may not be a value so using the "!" we tell the compiler we know it may or it may not be a value inside that variable BUT we know there will be one, even if it's an EMPTY STRING, unlike other programming languages that consider empty strings, or empty arrays like "false". That's not the case in swift.
Your expression inside a conditional statement MUST be a valid boolean result.
You are checking if the value is different of nil and returning if it is, based un your comments and your second if you probably want to check if it is nil.
println("Checking login details")
if(userEmail.isEmpty || userPassword.isEmpty || userRepeatPassword.isEmpty){
displayMyAlertMessage("All fields are required")
println("Fail to login not all fields where fill")
return
} else if(userPassword != userRepeatPassword){
displayMyAlertMessage("Passwords do not match.")
println("Fail to login password does not match")
} else {
var uiAlert = UIAlertController(title: "Alert", message: "Registration was successful", preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(uiAlert, animated: true, completion: nil)
uiAlert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { action in
dismissViewControllerAnimated(true, completion:nil)
}))
}
With optional booleans, it works a little differently, you need to check explicitly if the value is nil. So that is exactly why your After: works and not your Before:
//now this is checking if your values are nil (empty) rather than not nil
//before if a user had valid fields, it would say that "All fields are required"
//now, this will work
if(userEmail == nil || userPassword == nil || userRepeatPassword == nil){
displayMyAlertMessage("All fields are required")
} else if(userPassword != userRepeatPassword){
displayMyAlertMessage("Passwords do not match.")
} else {
//success
//perform segue here to correct screen
}
There are a couple of options of how you can perform a segue, I will choose to use the presentViewController method, see below on how to integrate that.
...
else {
//success
//perform segue here to correct screen
presentViewController(yourMainScreenViewController, animated: true, completion: nil)
}
You could also use the
performSegueWithIdentifier("yourMainScreenIdentifier", sender: nil) if you don't use the presentViewController method, like:
else {
//success
//perform segue here to correct screen
performSegueWithIdentifier("yourMainScreenIdentifier", sender: nil)
}
I will add in what I assume your displayMyAlertMessage is:
func displayMyAlertMessage(alert: String) {
println(alert)
}