Swift Firebase login - ios

I'm having a problem where every time I enter the right credentials, it brings me to one view controller then opens up the same view controller again even though I only have the login viewer controller linked to one view controller. If I don't enter the right credentials it still brings me into the linked view controller. Here is the code.
EDIT: Using a push segue(show)
#IBAction func loginTapped(_ sender: Any) {
if let Email = userEmail.text, let Pass = userPassword.text{
Auth.auth().signIn(withEmail: Email, password: Pass, completion: { (user, error) in
if error != nil{
print("incorrect")
}
else{
if error == nil{
self.performSegue(withIdentifier: "loginPage", sender: self)
print("correct")
}
}
})
}
}

I don't know if you've fixed your problem, but check your storyboard. Sounds like you have a segue connected from the button to the next ViewController which would result in pressing the button and it'll always push that ViewController.
To do this easily just see if you have a segue connected from the button to your destination ViewController in your MainStoryboard.

Related

How do I stop a segue from going through programmatically in Swift?

Here is the code:
#IBAction func loginTapped(_ sender: Any) {
let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
print(error!.localizedDescription)
}
else {
self.performSegue(withIdentifier: "loginSegue", sender: nil)
print("User is signed in with Firebase.")
}
}
}
I have a segue, loginSegue, connected from the login button to the homeViewController. Within in the if error statement I would like to stop the segue from going through because the user has not signed in. The goal here is not allow the user to go forward if they get an error. Is there an "opposite" to the performSegue(withIdentifier: String, sender: Any?) ?
First , there is no "opposite" to performSegue(withIdentifier: String, sender: Any?).
But the issue is not about this. I think you wired the segue from the login button and gave it an identifier. If you wire a segue from a button directly the button is always going to execute that segue. Doing some operations in the button's action does not effect.
You need to wire a segue from FirstVc to SecondVc (not from the button) and then give the segue an identifier. Then, from the button's action you can check if there is no error and call performSegue(withIdentifier: String, sender:) passing your segue's identifier.
I think your button is connected to perform segue in storyboard. So your button has two actions - one from storyboard to perform segue and second in your code. Just remove the connection from storyboard and connect only UIViewControllers not with your button.
You could override the shouldPerformSegue(withIdentifier:,sender:) method and return false if the login fails and you don't want to perform the segue. Here's an example
override func shouldPerformSegue(withIdentifier identifier: String?, sender: Any?) -> Bool {
if let ident = identifier {
if ident == "YourIdentifier" {
if loginSuccess != true {
return false
}
}
}
return true
}

UserDefaults Auto-Login isn't Working

So when the user first opens my app they will see a view with a button to proceed with logging in or registering. In my Login view, I have a button handler (attached via action) which is called when a user presses a login button. Despite me using UserDefaults to store logged in state, my app does not go straight to my Dashboard view automatically. Any ideas why?
// Firebase auth in doLogin action (Login VC)
Auth.auth().signIn(withEmail: un, password: pw)
{
(resp, err) in if (err == nil && resp != nil)
{
if (resp!.user.isEmailVerified)
{
// segue from Login VC to Dashboard VC will be exectuted
self.performSegue(withIdentifier: "loggedin", sender: nil)
// store the logged in state as true for the future
UserDefaults.standard.set(true, forKey: "loggedin")
UserDefaults.standard.synchronize()
}
}
}
Here is the code for the Main view controller to determine if it should proceed directly to dashboard.
func loggedIn() -> Bool {
// determine if the user is still logged into the app
return UserDefaults.standard.bool(forKey: "loggedin")
}
override func viewDidLoad() {
super.viewDidLoad()
if (loggedIn())
{
// logged in, return to the dashboard view controller
performSegue(withIdentifier: "persisted", sender: nil)
}
}
I've tried using print statements and either the loggedIn function isn't being executed for whatever reason or the console is just too flooded with Firebase debug log statements to even see these.
You have to embed your main view controller inside a UINavigationController to make the persisted segue work.

Segue Still Goes Through Despite Conditional Statement

I'm new to swift and Xcode. I creating a user registration view controller and I'm using Firebase as my DB. Here is what I've written:
#IBAction func registerButton(_ sender: Any) {
//Register user functions
Auth.auth().createUser(withEmail: emailField.text! , password: passwordField.text!) { (user, error) in
if error != nil {
print (error!)
} else {
//success
print ("Registration successful")
self.performSegue(withIdentifier: "registerToList", sender: self)
}
}
}
When a user inputs data that is incorrectly formatted, such as blank fields or improperly formatted email, my console prints out the error. However, the user is still able to bypass the registration view controller and move on to the next screen. Why is that?
I also want to note that I'm using a Show segue - is this the issue? I can't use a push segue since it has been deprecated.

whose view is not in the window hierarchy Facebook Login

I have this warning I don't understand where is my error in my code
using code 7.2 and iOS 9.2
Error:
warning: Attempt to present < VidcustCustomAppIos.VCTabBarController:
0x7ff838f50790 > on < FBSDKContainerViewController: 0x7ff838d8ceb0 >
whose view is not in the window hierarchy!
#IBAction func facebookLoginDidTouch(sender: AnyObject) {
let loginManager = FBSDKLoginManager()
loginManager.logInWithReadPermissions(["public_profile", "email"], fromViewController: self) { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if (result.isCancelled) {
print("Cancelled")
} else {
print("Logged in")
let token = FBSDKAccessToken.currentAccessToken()
VCClusterManager.sharedInstance.connectWithFacebook(token.tokenString, completion: { (isConnected, error) -> () in
if error == nil && isConnected == true {
self.performSegueWithIdentifier(self.segueIdentifier, sender: nil)
}
})
}
}
}
This is not with the case of Facebook Login
You will get this warning when performing a segue from a view controller that is embedded in container. The solution is to use segue from the parent of container, not from container's view controller.
Error:
Developers can receive this warning when performing a segue from a view controller that is embedded in container.
Solution:
Use segue from the parent of container, not from container's view controller (eg. back button, next button or any buttons that you want to perform an action to transition to another view controller)

Swift performSegueWithIdentifier not working

I am trying to switch view controllers after a user successfully logs in to their account, but it is not working correctly. I cant use a segue directly because if the login button is clicked it will go to that view controller regardless if the information is correct or not. I have tried everything that I know of with no success. This is the code I am trying.
#IBAction func loginTapped(sender: AnyObject) {
let username = usernameField.text
let password = passwordField.text
if username.isEmpty || password.isEmpty {
var emptyFieldsError:UIAlertView = UIAlertView(title: "Please try again", message: "Please fill in all the fields we can get you logged in to your account.", delegate: self, cancelButtonTitle: "Try again")
emptyFieldsError.show()
}
PFUser.logInWithUsernameInBackground(username, password:password) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
self.performSegueWithIdentifier("Klikur", sender: self)
} else {
if let errorString = error!.userInfo?["error"] as? String {
self.errorMessage = errorString
}
self.alertView("Please try again", message: "The username password combiation you have given us does not match our records, please try again.", buttonName: "Try again")
}
}
}
I have the storyboard ID set to "Test" and it is not switching view controller when the correct information is entered. Can somebody help me resolve my problem?
[Assuming that your code is not crashing, but rather just failing to segue]
At least one problem is:
self.performSegueWithIdentifier("Test", sender: self)
should be:
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.performSegueWithIdentifier("Test", sender: self)
}
Remember that all UI operations must be performed on the main thread's queue. You can prove to yourself you're on the wrong thread by checking:
NSThread.isMainThread() // is going to be false in the PF completion handler
ADDENDUM
If there's any chance self might become nil, such as getting dismissed or otherwise deallocated because it's not needed, you should capture self weakly as [weak self] not unowned, and use safe unwrapping: if let s = self { s.doStuff() } or optional chaining: self?.doStuff(...)
ADDENDUM 2
This seems to be a popular answer so it's important to mention this newer alternative here:
NSOperationQueue.mainQueue().addOperationWithBlock {
[weak self] in
self?.performSegueWithIdentifier("Test", sender: self)
}
Note, from https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift:
NSOperation vs. Grand Central Dispatch (GCD)
GCD [dispatch_* calls] is a lightweight way to represent units of work that are going to be executed concurrently.
NSOperation adds a little extra overhead compared to GCD, but you can add dependency among various operations and re-use, cancel or suspend them.
ADDENDUM 3
Apple hides the single-threaded rule here:
NOTE
For the most part, use UIKit classes only from your app’s main thread.
This is particularly true for classes derived from UIResponder or that
involve manipulating your app’s user interface in any way.
SWIFT 4
DispatchQueue.main.async(){
self.performSegue(withIdentifier: "Test", sender: self)
}
Reference:
https://developer.apple.com/documentation/uikit
Make sure you're putting your:
self.performSegue(withIdentifier: ..., ...)
in viewDidAppear or later. It won't work in viewWillAppear or viewDidLoad.
I've got the same problem with login issue. probably we do the same tutorial. After naming your segue identifier you need to replace:
performSegueWithIdentifier("Klikur", sender: self)
with:
dispatch_async(dispatch_get_main_queue()){
self.performSegueWithIdentifier("Klikur", sender: self)
}
type of seque needs to be set as "show (e.g. Push)" in the storyboard segue.
Hope it will work.
The segue identifier that you pass to performSegueWithIdentifier(_:sender:) must exactly match the ID you've given the segue in the storyboard. I assume that you have a segue between the login view controller and the success view controller, which is as it should be; if not, ctrl+drag from the first to the second view controller, then select the segue's icon in the storyboard and set its ID to Klikur. Don't perform the navigation on the button click, as one commenter said, because that defeats the main purpose of having segues, which is to give a visual indication of the application flow in the storyboard.
EDIT: Here's the code for a login view controller:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBAction func attemptLogin(sender: AnyObject) {
if !usernameField!.text!.isEmpty && !passwordField!.text!.isEmpty {
performSegueWithIdentifier("Klikur", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if "Klikur" == segue.identifier {
// Nothing really to do here, since it won't be fired unless
// shouldPerformSegueWithIdentifier() says it's ok. In a real app,
// this is where you'd pass data to the success view controller.
}
}
}
And a screenshot of the segue properties that I'm talking about:
swift 3.x
DispatchQueue.main.async(){
self.performSegue(withIdentifier: "Klikur", sender: self)
}
DispatchQueue.main.async() {
self.performSegue(withIdentifier: "GoToHomeFromSplash", sender: self)`
}
Check to make sure you are running the perform segue on a visible view controller.
This is an edge case, but my perform segue failed when I attempted to run it on the view controller belonging to my UIPageViewController that was not currently visible. It also failed if I attempted to do the segue on all view controllers belonging to my UIPageViewController, including the view controller currently visible. The fix was to track which view controller was currently visible in my UIPageViewController, and only perform the segue on that view controller.
An example in a login. When you have success in your login after clicking a button (Action) you can use:
self.performSegue(withIdentifier: "loginSucess", sender: nil)
But if you are launching the app and you got the credentials from your keychain you need to use this as a part of the theard:
DispatchQueue.main.async(){
self.performSegue(withIdentifier: "sessionSuccess", sender: nil)
}

Resources