Code not finishing the function, it is ending execution halfway through - swift

My code is as follows:
#IBAction func clicked(_ sender: Any) {
let ref = Database.database().reference()
let pass = password.text
var firpass = ""
var bool = false;
ref.child(name.text as! String).child("password").observeSingleEvent(of: .value, with: { dataSnapshot in
firpass = dataSnapshot.value as! String
if firpass == pass {
bool = true
print("in here")
}
})
print(bool)
if bool {
self.sendname = name.text!
let vc = DatabaseTableViewController(nibName: "DatabaseTableViewController", bundle: nil)
vc.finalName = self.sendname
navigationController?.pushViewController(vc, animated: true)
performSegue(withIdentifier: "username", sender: self)
} else {
let alert = UIAlertController(title: "Error", message: "Incorrect username or password", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
"in here" gets printed, but bool is never printed and the alert is showing. Why does my code not enter the if bool block and output the alert?

Data is loaded from Firebase asynchronously, since it may take a while. Instead of making your app wait for the data (which would be a bad user experience), your main code continues while the data is being loaded, and then once the data is available your closure is called.
This explains the behavior you're seeing: by the time your runs, the hasn't run yet.
the solution is as simple as it is initially confusing and annoying: any code that needs the data from the database must be inside the closure, or be called from there.
So for example:
ref.child(name.text as! String).child("password").observeSingleEvent(of: .value, with: { dataSnapshot in
firpass = dataSnapshot.value as! String
if firpass == pass {
bool = true
print("in here")
}
print(bool)
if bool {
self.sendname = name.text!
let vc = DatabaseTableViewController(nibName: "DatabaseTableViewController", bundle: nil)
vc.finalName = self.sendname
navigationController?.pushViewController(vc, animated: true)
performSegue(withIdentifier: "username", sender: self)
} else {
let alert = UIAlertController(title: "Error", message: "Incorrect username or password", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
})
Also see:
Firebase with Swift 3 counting the number of children
Array of struct not updating outside the closure
getting data out of a closure that retrieves data from firebase (showing examples with custom callbacks and delegates)
How to reload data after all Firebase calls finished? (showing how to use a dispatch group)
Finish all asynchronous requests before loading data? (another example using a dispatch group)

Also you have to set variable bool to false when you are navigating to next view controller after login. So that you login again and if password is wrong then you can not navigate to next page and only it shows alert for wrong password.

Related

having trouble trying to keep users logged in with addStateDidChangeListener()

So my goal for now is to successfully keep users logged in and show a certain viewController depending if they're logged in or not. I've read a lot of the Stack questions that showed up first on Google searches about this same topic and they said use addStateDidChangeListener() and that's exactly what I did.
I didn't know how to approach this, so I watched a Youtube video and copied the exact code the guy had, his project did what I wanted mine to do, so I gave it a shot. Unfortunately when I run the simulator, sign in, exit the simulator and simulate again, nothing changes. I will add my code and it's location.
This is the code in my AppDelegate.swift in the didFinishLaunchingWithOptions method
let storyboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let auth = Auth.auth()
auth.addStateDidChangeListener { (_, user) in
switch user {
case nil:
guard self.activeViewController! is StudentSegmentedTableViewController else { return }
let nonLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
self.navigationController.setViewControllers([nonLoggedInViewController], animated: false)
self.navigationController.popToViewController(nonLoggedInViewController, animated: true)
self.activeViewController = nonLoggedInViewController
default:
guard self.activeViewController! is GothereMainMenuViewController else { return }
let alreadyLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
self.navigationController.setViewControllers([alreadyLoggedInViewController], animated: false)
self.navigationController.popToViewController(alreadyLoggedInViewController, animated: true)
self.activeViewController = alreadyLoggedInViewController
}
}
let nonLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
let alreadyLoggedInViewController = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
activeViewController = nonLoggedInViewController
switch Auth.auth().currentUser != nil {
case true:
activeViewController = alreadyLoggedInViewController
default:
break
}
navigationController = UINavigationController.init(rootViewController: activeViewController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
I tried just this alone at first, and it didn't work so then I implemented a state listener in reasonable spots in my app.
First I added one that enables right after successful log in/signup and the segue is performed .
func enableAuth() {
authListener = Auth.auth().addStateDidChangeListener { (_, user) in
print("State Listener activated")
}
}
This is what I call in the viewDidLoad() of the segued viewController right after login/signup. To remove it, I simply call it when the logout button is pressed..
func disableAuthState() {
Auth.auth().removeStateDidChangeListener(self.authListener!)
print("State Listener Deactivated")
}
func studentLogoutSelected() {
var text = UITextField()
let alert = UIAlertController(title: "Logout", message: "Are you sure you want to logout?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
self.dismiss(animated: true, completion: nil)
}
let logoutAction = UIAlertAction(title: "Logout", style: .default) { (logoutAction) in
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
self.disableAuthState()
self.performSegue(withIdentifier: Constants.Segues.studentLogout, sender: self)
} catch let signOutError as NSError {
print("There was an error signing the user out. \(signOutError)")
}
}
alert.addAction(cancelAction)
alert.addAction(logoutAction)
present(alert, animated: true, completion: nil)
}
After all these functions and implementations, the shown blocks of code still don't do what I expected them to do. If anybody can point out issues or suggestions, that would be great, thanks.
First of all are you add FirebaseApp.configure() on your didFinishLaunchingWithOptions function in appdelegate? Then, Can you try call enableAuth in viewWillAppear()

AWS Cognito doesn't call confirmSignUp when confirming user identity in Swift with Xcode

I finally managed to get the SignUp to work, but when trying to confirm the signup, I'm reaching a problem. Here is my code:
var user: AWSCognitoIdentityUser?
#IBAction func submitButton(_ sender: Any) {
guard let confirmationCodeValue = self.codeTextField.text, !confirmationCodeValue.isEmpty else {
let confirmationAlert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
confirmationAlert.addAction(UIAlertAction(title: "好", style: .default, handler: {action in
print("Try again!")
}))
self.present(confirmationAlert, animated: true, completion: nil)
return
}
self.user?.confirmSignUp(self.codeTextField.text!, forceAliasCreation: true).continue({[weak self] (task: AWSTask) -> Any? in
guard let strongSelf = self else { return nil }
DispatchQueue.main.async(execute: {
print("At least this is working...")
if let error = task.error {
let confirmationFailAlert = UIAlertController(title: (error as NSError).userInfo["__type"] as? String,
message: (error as NSError).userInfo["__type"] as? String,
preferredStyle: .alert)
confirmationFailAlert.addAction(UIAlertAction(title: "好",
style: .default,
handler: {action in
}))
self?.present(confirmationFailAlert, animated: true, completion: nil)
} else {
let confirmationSuccessAlert = UIAlertController(title: self?.alertTitleConfirmationComplete,
message:self?.alertMessageConfirmationComplete,
preferredStyle: .alert)
confirmationSuccessAlert.addAction(UIAlertAction(title: "好",
style: .default,
handler: {action in
self?.dismiss(animated: true, completion: nil)
}))
self?.present(confirmationSuccessAlert, animated: true, completion: nil)
}
})
return nil
})
}
The first part of this code works fine. If I type nothing in the space, I get an alertView telling me so. However, if I type anything in the space, nothing happens. The print statement "At least this is working..." never gets called. I've been staring at this code for a couple hours now trying to figure out what's wrong, and I feel like it's probably something simple, but as of now, I could use some help!
Thanks in advance!
I assume the code block above is not the full source, but be sure the optional, user, is "unencapsulated" and equal to an actual instance of AWSCognitoIdentityUser.
If it isn't, which I am assuming it is not, confirmSignUp won't know the username, sub, or have any information on the user it is "confirming".
I would recommend logging user and be sure that username is in fact a value within user.
I believe you set it equal to that instance type in the response to your AWSCognitoIdentityUserPool class signUp:password:userAttributes:validationData: call.
Check those values that are returned in AWSCognitoIdentityUserPoolSignUpResponse.

IPv6 Crash Firebase Swift

I have a Swift app using Firebase as backend.
It works perfectly fine on all simulators and my own iOS devices, but when I submitted it to Apple for review, it crashed specifically when the reviewer tapped on a UIButton that presents a UIAlertController containing my content reporting mechanism:
#IBAction func moreActions(_ sender: AnyObject) {
// 1
let optionMenu = UIAlertController(title: nil, message: "Choose Action", preferredStyle: .actionSheet)
// 2
let reportPost = UIAlertAction(title: "Report this post", style: .default, handler: {
(alert: UIAlertAction!) -> Void in
self.report()
})
// 3
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {
(alert: UIAlertAction!) -> Void in
print("Cancelled")
})
// 4
optionMenu.addAction(reportPost)
optionMenu.addAction(cancelAction)
self.present(optionMenu, animated: true, completion: nil)
}
func report() {
let messageView = MessageView.viewFromNib(layout: .CardView)
var config = SwiftMessages.Config()
config.dimMode = .gray(interactive: true)
messageView.configureTheme(.info)
messageView.button?.isHidden = true
messageView.configureContent(title: "Thank you", body: "We have received your report, and will soon make all necessary actions.")
SwiftMessages.show(config: config, view: messageView)
ref = FIRDatabase.database().reference()
ref.child("reportedPost").observeSingleEvent(of: .value, with: {(snapshot) in
if snapshot.hasChild(self.passedPurchaseKey) {
print("this post has been reported before")
let targetKey = self.passedPurchaseKey
let value = snapshot.childSnapshot(forPath: targetKey!).value as! Int
let newValue = value + 1
self.ref.child("reportedPost").updateChildValues([targetKey!: newValue])
}else{
print("this post has not been reported before")
let targetKey = self.passedPurchaseKey
self.ref.child("reportedPost").updateChildValues([targetKey!: 1])
}
})
}
The reviewer suggested that the crash might due to IPv6 incompatibility. I cannot agree with them since the networking logics above are very similar to the rest of my App, which did not lead to any crashes. Thus I suspect that the crash was due to something else.
Yet I could not be sure because I do not have the required hardwares to test my app under IPv6 environment.
Thanks in advance for your kind help!
//edit 1: crash log added
crashlog_1
crashlog_2

How to get text input out of UIAlertcontroller OR how to wait for the input using Swift

I am trying to present an Alertcontroller that prompts the user for a filename and then use the filename elsewhere in the program. I have been trying numerous variations of the following code:
import UIKit
class ViewController: UIViewController {
var shortName: String!
#IBAction func saveFile(sender: AnyObject) {
//request filename with alert
var alertController:UIAlertController?
alertController = UIAlertController(title: "Enter File",
message: "Enter file name below",
preferredStyle: .Alert)
alertController!.addTextFieldWithConfigurationHandler(
{(textField: UITextField!) in
textField.placeholder = ""
})
let action = UIAlertAction(title: "Submit",
style: UIAlertActionStyle.Default,
handler: {[weak self]
(paramAction:UIAlertAction!) in
if let textFields = alertController?.textFields{
let theTextFields = textFields as [UITextField]
let enteredText = theTextFields[0].text
self!.shortName = enteredText //trying to get text into shortName
print(self!.shortName) // prints
}
})
alertController?.addAction(action)
self.presentViewController(alertController!,
animated: true,
completion: nil)
//do some stuff with the input
print(shortName) //THIS RETURNS nil if uncommented. comment out to avoid crash
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I have thoroughly researched this and can't find how to either:
get the string value of shortName out of the UIAlertAction closure and into the shortName (I know that the current "self!.shortName" is unavailable outside the closure - doesn't matter what name I use - can't get it out)
If you run the program "as-is", the print(shortName) line will cause a crash due to unwrapping of nil. How can I get the alert to "wait" for input?
Most of the posted "solutions" have the same problem - they don't actually get the text input out of the closure and into a variable that can be accessed by the rest of the program.
thanks
Of course you get crash, shortName is nil while Submit button isn't pressed. You can try something like this:
#IBAction func saveFile(sender: AnyObject) {
var alertController:UIAlertController?
alertController = UIAlertController(title: "Enter File",
message: "Enter file name below",
preferredStyle: .Alert)
alertController!.addTextFieldWithConfigurationHandler(
{(textField: UITextField!) in
textField.placeholder = ""
})
let action = UIAlertAction(title: "Submit",
style: UIAlertActionStyle.Default,
handler: {[weak self]
(paramAction:UIAlertAction!) in
if let textFields = alertController?.textFields{
let theTextFields = textFields as [UITextField]
let enteredText = theTextFields[0].text
self!.shortName = enteredText //trying to get text into shortName
print(self!.shortName) // prints
self?.handleText()
NSOperationQueue.mainQueue().addOperationWithBlock({
self?.handleTextInMainThread()
})
}
})
alertController?.addAction(action)
self.presentViewController(alertController!,
animated: true,
completion: nil)
}
func handleText() {
print(self.shortName)
}
func handleTextInMainThread() {
print(self.shortName)
}
You have use NSOperationQueue if you want to work with UI inside handleTextInMainThread after user's input.
I think it is timing. You tried to print shortName right after presenting the alert. At that time the value is not set yet.
You can either use semaphore to wait till it is set or do whatever you want to do in the action closure for "submit".

"fatal error: unexpectedly found nil while unwrapping an Optional value" in Standardized Social Media Sharing Method

I am trying to create a standardized social media sharing method inside my SocialMediaSharingDelegate. Specifying an individual social media service like I do in the facebookShare method works perfectly fine. So to extend this I created an enum with the possible service types and the shareOn method accepts any valid service name to perform sharing.
It seems straight forward. However, when I run the code and click on a button that calls the shareOn method I get a runtime error. The error "fatal error: unexpectedly found nil while unwrapping an Optional value" occurs on the line let serviceViewController... right after the guard statement.
It also works fine if I replace socialMediaService.associatedService() with SLServiceTypeFacebook on that line which is strange because socialMediaService.associatedService() is used without issue in the guard statement.
I can just create separate functions for each service for now but that is not ideal in terms of code reuse.
import Foundation
import UIKit
import Social
enum SocialMediaEnum : String {
case Twitter = "SLServiceTypeTwitter"
case Facebook = "SLServiceTypeFacebook"
// This internal enum function returns the SLServiceType associated with the current enum value
func associatedService() -> String {
var serviceName: String
switch self {
case .Twitter:
serviceName = "SLServiceTypeTwitter"
case .Facebook:
serviceName = "SLServiceTypeFacebook"
}
return serviceName
}
}
class socialMediaSharing : SocialMediaSharingDelegate {
func shareOn(let socialMediaService: SocialMediaEnum, sender: subclassedUIButton?, currentViewController: UIViewController) {
guard SLComposeViewController.isAvailableForServiceType(socialMediaService.associatedService()) else {
let alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to tweet.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
currentViewController.presentViewController(alert, animated: true, completion: nil)
return
}
let serviceViewController :SLComposeViewController = SLComposeViewController(forServiceType: socialMediaService.associatedService())
if let imageUrl = sender?.urlString {
if let url:NSURL = NSURL(string: imageUrl as String) {
serviceViewController.addURL(url)
}
}
currentViewController.presentViewController(serviceViewController, animated: true, completion: nil)
}
func facebookShare(sender: subclassedUIButton?, currentViewController: UIViewController) {
guard SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook) else {
let alert = UIAlertController(title: "Accounts", message: "Please login to a Facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
currentViewController.presentViewController(alert, animated: true, completion: nil)
return
}
let shareToFacebook :SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
if let imageUrl = sender?.urlString {
if let url:NSURL = NSURL(string: imageUrl as String) {
shareToFacebook.addURL(url)
}
}
currentViewController.presentViewController(shareToFacebook, animated: true, completion: nil)
}
}
SLServiceTypeTwitter and SLServiceTypeFacebook are keywords so they should not have quotation marks in the switch statement. The actual values they hold are "com.apple.social.(service name)".