Cant segue new facebook auth user with Parse - ios

I'm registering new users under my application that uses facebook API and Parse. The users are being created without segueing to another view. Unfortunately, if I try to segue for a next view the user is created without the name and email info. I know this runs in background so i need a solution to know when the execution is finished. Any ideas?
override func viewDidLoad() {
FBSDKProfile.enableUpdatesOnAccessTokenChange(true)
}
override func viewDidAppear(animated: Bool) {
if let access = FBSDKAccessToken.currentAccessToken() {
PFFacebookUtils.logInInBackgroundWithAccessToken(access, block: {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
println("already registred user")
self.performSegueWithIdentifier("firstView", sender: self)
} else {
println("Uh oh. There was an error logging in.")
}
})
}
}
#IBAction func fbLoginButtonTouchUpInside (sender: AnyObject) {
PFFacebookUtils.logInInBackgroundWithReadPermissions(["public_profile", "email", "user_friends"], block: { (user, error) in
if let user = user {
User.updateUserInfo()
println("new user")
//THIS SEGUE IS CRASHING
self.performSegueWithIdentifier("newUserSegue", sender: self)
} else {
println("Uh oh. The user cancelled the Facebook login.")
}
})
}

Try this. AFAK you must perform on main thread that is why it is crashing:
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("newUserSegue", sender: self)
}

The problem here was that I was running another background task inside
PFFacebookUtils.logInInBackgroundWithReadPermissions
The solution was to add a completion handler to User.updateUserInfo() and perform the segue inside of it.

Related

Segue is not being performed

I've created a method for auto login via Firebase but somehow my segue is not being performed..
I've this code and in my viewDidLoad I'm calling the method (ofc)
override func viewDidLoad() {
super.viewDidLoad()
//Login user automatically
autoLogin()
}
func autoLogin(){
if Auth.auth().currentUser?.email != nil{
print("not nil") //for test
self.performSegue(withIdentifier: "toRootVc", sender: self)
print("not nil1") //for test
}
else{
print("nil") //for test
}
}
The app prints both "not nil" and "not nil1" but it still does not
performing the segue.
I also have a login button which works.
func handleLogin(){
Auth.auth().signIn(withEmail: email.text!, password: password.text!) { (result, err) in
if let err = err{
print("Error logging in:", err.localizedDescription)
}
else{
self.databaseHandler.retrieveData(email: self.email.text!){
self.performSegue(withIdentifier: "toRootVc", sender: self)
}
}
}
}
But the autoLogin doesn't actually performing the segue. (Ignores the step)
Any input would be much appreciated.
Segues won't work inside viewDidLoad as it's too early try in viewWillAppear or better do this check before presenting that vc say inside didFinishLaunchingWithOptions of AppDelegate
have you tried to put this autoLogin() on viewDidAppear()

Swift. Detecting when facebook login viewcontroller is being dismissed

I'm trying to detect when the facebook login viewcontroller gets dismissed so I can stop the loading animation. I implemented the facebook login SDK through firebase, and I'm logging in using this method:
#IBAction func facebookSignIn(_ sender: UIButton) {
loginBtn.startLoadingAnimation()
FBSDKLoginManager().logIn(withReadPermissions: ["email"], from: self) { (result, err) in
if err != nil {
print("CustomFB Login Failed: ", err)
self.loginBtn.stopLoadingAnimation()
return
}
}
}
How would I detect when the login viewcontroller gets dismissed?
This is simple, you have put stopLoadingAnimation() in the wrong place.
#IBAction func facebookSignIn(_ sender: UIButton) {
loginBtn.startLoadingAnimation()
FBSDKLoginManager().logIn(withReadPermissions: ["email"], from: self) { (result, err) in
self.loginBtn.stopLoadingAnimation()
//Facebook login is complet after Two case, failer and success.
if err != nil {
print("CustomFB Login Failed: ", err)
return
}
}
}
Stop spinner login is under if condition, but user clicks on cancel then your spinner does not stop.

Being able to login even though login is wrong

I am using Firebase for Login/Sign Up authentication but I ran into a problem. I got everything to set up and it works fine, but I am having a bit of an issue with the login part.
Here's my code:
#IBAction func clickLogin(_ sender: UIButton) {
FIRAuth.auth()?.signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if let error = error {
print(error.localizedDescription)
}
})
performSegue(withIdentifier: "toMainSegue", sender: self) //Issue
}
What's wrong is that when the email or the password is incorrect, it will still perform the segue. I tried:
#IBAction func clickLogin(_ sender: UIButton) {
FIRAuth.auth()?.signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if let error = error {
print(error.localizedDescription)
} else {
performSegue(withIdentifier: "toMainSegue", sender: self) //Error Line
}
})
But I get an error:
Implicit use of ‘self’ in closure, use ‘self.’ to capture semantics explicit.
Is there a better way of bring the user to the next UI if and only if login was successful?
In the code that you have shared
#IBAction func clickLogin(_ sender: UIButton) {
FIRAuth.auth()?.signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if let error = error {
print(error.localizedDescription)
}
})
performSegue(withIdentifier: "toMainSegue", sender: self) //Issue
}
The performSegue(withIdentifier:sender:) method is being called within the #IBAction and not inside the completion handler of the signIn(withEmail:password:completion) method. Thus, regardless of what is written or executed in the latter, your performSegue(withIdentifier:sender:) will be called. Try modifying the code to the following
#IBAction func clickLogin(_ sender: UIButton) {
FIRAuth.auth()?.signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if let error = error {
print(error.localizedDescription)
} else {
self.performSegue(withIdentifier: "toMainSegue", sender: self)
}
})
}
Keep in mind that, because the logic is being executed in a closure, you need to specify the self. prefix before methods and variables!
Any variables or methods used inside of block needs to use of ‘self’.
#IBAction func clickLogin(_ sender: UIButton) {
FIRAuth.auth()?.signIn(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if let error = error {
print(error.localizedDescription)
} else {
self.performSegue(withIdentifier: "toMainSegue", sender: self) //Error Line
}
})
Your code goes in the else part everytime your API hits successfully even if the login credentials are wrong.
The FIRAuth API must be returning some data when it gets hit, for example a string or dictionary named "success" = 1 or 0. Check in ur else part for the success to be true or false. false being wrong credentials and true being correct credentials.
The error part gets executed when there is any error in hitting the API itself like network error or the API's parameters being in wrong format or any other error.
In your case its getting hit and returning a result too. You have to check the result dictionary if your user did get logged in or not and segue onto the next controller based on that result.
Try this. This is how I do my login.
FIRAuth.auth()?.signIn(withEmail: emailField.text!, password: passwordField.text!, completion: { user, error in
if error == nil {
print("Successfully Logged IN \(user!)")
self.performSegue(withIdentifier: "signedIn", sender: self)
}
})
This just tests if there is no error with the signing in process, then performs the segue. I haven't had any trouble with it, and it seems to work great.

How to check if user has valid Auth Session Firebase iOS?

I wanna check if the user has still a valid session, before I present the Home View controller of my app. I use the latest Firebase API. I think if I use the legacy, I'll be able to know this.
Here's what I did so far:
I posted my question on Slack community of Firebase, no one is answering. I found this one, but this is for Android: https://groups.google.com/forum/?hl=el#!topic/firebase-talk/4HdhDvVRqHc
I tried reading the docs of Firebase for iOS, but I can't seem to comprehend it: https://firebase.google.com/docs/reference/ios/firebaseauth/interface_f_i_r_auth
I tried typing in Xcode like this:
FIRApp().currentUser()
FIRUser().getCurrentUser()
But I can't seem to find that getCurrentUser function.
if FIRAuth.auth().currentUser != nil {
presentHome()
} else {
//User Not logged in
}
For updated SDK
if Auth.auth().currentUser != nil {
}
Updated answer
Solution for latest Firebase SDK - DOCS
// save a ref to the handler
private var authListener: AuthStateDidChangeListenerHandle?
// Check for auth status some where
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
authListener = Auth.auth().addStateDidChangeListener { (auth, user) in
if let user = user {
// User is signed in
// let the user in?
if user.isEmailVerified {
// Optional - check if the user verified their email too
// let the user in?
}
} else {
// No user
}
}
}
// Remove the listener once it's no longer needed
deinit {
if let listener = authListener {
Auth.auth().removeStateDidChangeListener(authListener)
}
}
Original solution
Solution in Swift 3
override func viewDidLoad() {
super.viewDidLoad()
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
if user != nil {
self.switchStoryboard()
}
}
}
Where switchStoryboard() is
func switchStoryboard() {
let storyboard = UIStoryboard(name: "NameOfStoryboard", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ViewControllerName") as UIViewController
self.present(controller, animated: true, completion: nil)
}
Source
Solution in Swift 4
override func viewDidLoad() {
super.viewDidLoad()
setupLoadingControllerUI()
checkIfUserIsSignedIn()
}
private func checkIfUserIsSignedIn() {
Auth.auth().addStateDidChangeListener { (auth, user) in
if user != nil {
// user is signed in
// go to feature controller
} else {
// user is not signed in
// go to login controller
}
}
}
if Auth.auth().currentUser?.uid != nil {
//user is logged in
}else{
//user is not logged in
}
While you can see if there is such a user using Auth.auth().currentUser, this will only be telling you if there was a user authenticated, regardless of whether that users account still exists or is valid.
Complete Solution
The real solution to this should be using Firebase's re-authentication:
open func reauthenticate(with credential: AuthCredential, completion: UserProfileChangeCallback? = nil)
This assures (upon the launch of the application) that the previously signed in / authenticated user still in fact is and can be authenticated through Firebase.
let user = Auth.auth().currentUser // Get the previously stored current user
var credential: AuthCredential
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
override func viewDidLoad() {
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
// 2
if user != nil {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Home")
self.present(vc!, animated: true, completion: nil)
}
}
}
Source: https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2
An objective-c solution would be (iOS 11.4):
[FIRAuth.auth addAuthStateDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
// your logic
}
}];
All the provided answers only check on currentUser. But you could check the auth session by simple user reload like below:
// Run on the background thread since this is just a Firestore user reload, But you could also directly run on the main thread.
DispatchQueue.global(qos: .background).async {
Auth.auth().currentUser?.reload(completion: { error in
if error != nil {
DispatchQueue.main.async {
// Authentication Error
// Do the required work on the main thread if necessary
}
} else {
log.info("User authentication successfull!")
}
})
}

get user information using Facebook custom login in Swift

I'm trying to get user information using a custom facebook login with the Facebook SDK for iOS. I successfully log in the user but I don't know how to get access to the user information.
This is my login function:
func facebookLogin() {
if (FBSession.activeSession().state == FBSessionState.Open || FBSession.activeSession().state == FBSessionState.OpenTokenExtended)
{
// Close the session and remove the access token from the cache
// The session state handler (in the app delegate) will be called automatically
FBSession.activeSession().closeAndClearTokenInformation()
}
else
{
// Open a session showing the user the login UI
// You must ALWAYS ask for public_profile permissions when opening a session
FBSession.openActiveSessionWithReadPermissions(["public_profile", "email"], allowLoginUI: true, completionHandler: {
(session:FBSession!, state:FBSessionState, error:NSError!) in
self.sessionStateChanged(session, state: state, error: error)
})
}
}
Then when the session is open the function sessionStateChanged is called:
func sessionStateChanged(session:FBSession, state:FBSessionState, error:NSError?){
if ((error) != nil){
NSLog("Error")
FBSession.activeSession().closeAndClearTokenInformation()
}
else{
if (state == FBSessionState.Open){
//I would like to get the user token or FBGraphUser here but i don't know how
}
}
if (state == FBSessionState.Closed || state == FBSessionState.ClosedLoginFailed){
NSLog("Session Clossed")
}
if (FBErrorUtility.shouldNotifyUserForError(error) == true){
NSLog("Something went wrong")
}
else{
if (FBErrorUtility.errorCategoryForError(error) == FBErrorCategory.UserCancelled){
NSLog("User cancelled login")
}
else if (FBErrorUtility.errorCategoryForError(error) == FBErrorCategory.AuthenticationReopenSession){
NSLog("Current session is no valid")
}
}
}
Ran into the exact same problem today! I'm not sure if this is the ideal way of doing things, but this is the workaround that my colleague showed me:
FBSession.openActiveSessionWithReadPermissions(["public_profile"], allowLoginUI: false) { session, state, error in
let request = FBRequest(session: FBSession.activeSession(), graphPath: "/me")
request.startWithCompletionHandler({ (connection, result, error) -> Void in
println(result)
let birthday = result.valueForKey("birthday") as String
println("\(birthday)")
})
self.fbSessionStateChanged(session, state: state, error: error)
}
Please forgive the ugly code, I'm still very much experimenting with Swift. Hope it helps!
You can use this:
Add FBLoginViewDelegate to the class
Declare this
#IBOutlet var fbLoginView : FBLoginView!
Connect this var (fbLoginView) with a new view in your storyboard. This new view can be hidden so it wont has any effect but helping you to track the information
In your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
//facebook login
self.fbLoginView.delegate = self
self.fbLoginView.readPermissions = ["public_profile", "email"]
}
And add this function
func loginViewFetchedUserInfo(LoginView : FBLoginView!, user: FBGraphUser){
println("User name: \(user.name)")
//THE USER WAS LOGGED
}
When the user is authenticated by facebook, returns to loginViewFetchedUserInfo method automatically by using the FBLoginViewDelegate and you can get the information with the "user" variable

Resources