Parse PFUser.currentUser returns nil - Swift - ios

I am logging in my users using Parse. As my app opens my LaunchVieWController determines whether users are already signed in or not:
override func viewDidLoad() {
super.viewDidLoad()
//Make sure cached users don't have to log in every time they open the app
var currentUser = PFUser.currentUser()
println(currentUser)
if currentUser != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("alreadySignedIn", sender: self)
}
} else {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("showSignUpIn", sender: self)
}
}
}
If users are already signed in they are taken to the table view described below. If they are not logged in, they are taken to a view in which they can go to the signup view or the login view.
My signup works perfectly fine, and signs up users and then redirects them to the table view.
Here is the signup function (it is in a separate controller from the login function):
func processSignUp() {
var userEmailAddress = emailAddress.text
var userPassword = password.text
// Ensure username is lowercase
userEmailAddress = userEmailAddress.lowercaseString
// Add email address validation
// Start activity indicator
activityIndicator.hidden = false
activityIndicator.startAnimating()
// Create the user
var user = PFUser()
user.username = userEmailAddress
user.password = userPassword
user.email = userEmailAddress
user.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("signInToNavigation", sender: self)
}
} else {
self.activityIndicator.stopAnimating()
if let message: AnyObject = error!.userInfo!["error"] {
self.message.text = "\(message)"
}
}
}
}
My login function looks like so:
#IBAction func signIn(sender: AnyObject) {
var userEmailAddress = emailAddress.text
userEmailAddress = userEmailAddress.lowercaseString
var userPassword = password.text
PFUser.logInWithUsernameInBackground(userEmailAddress, password:userPassword) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("signInToNavigation", sender: self)
}
} else {
if let message: AnyObject = error!.userInfo!["error"] {
self.message.text = "\(message)"
}
}
}
}
After users log in they are sent to a table view. In that table view I am trying to access the current user object in the following function, as I want to load different data based on who the user is:
override func queryForTable() -> PFQuery {
var query = PFQuery(className:"MyClass")
query.whereKey("userID", equalTo: PFUser.currentUser()!)
return query
}
When I try to log in with a user I get thrown the following error: "fatal error: unexpectedly found nil while unwrapping an Optional value". It seems like the PFUser.currentUser() object is nil.
When users are sent to the table view after signing up the PFUser.currentUser() object is set and works perfectly.
My guess is that this is because of the fact that the PFUser.logInWithUsernameInBackground is happening in the background and that my query is trying to get the PFUser.currentUser() object before it has been loaded. Is there anyway I can work around this issue? In the table view the value of PFUser.currentUser() is needed to load the table data. Can I somehow make sure that PFUser.currentUser() gets assigned with the current user object before the function gets called (for example, by not loading in users in the background thread)?
All help is much appreciated!
EDIT: I've updated this post with some more of my code to help highlight any bug that I may be missing.

I discovered that this problem appeared because the segue signInToNavigation was wired from the Login-button, instead from the login view controller itself. This resulted in the segue being executed before the login function was executed, and therefore the PFUser.currentUser()object was not yet assigned when the table view loaded. I solved this by rewiring the segues. Stupid slip-up on my side.
Thanks to Ryan Kreager and Wain for taking time to help me figure out this issue!

You can try checking if PFUser.currentUser() != nil instead to make sure it's being set right after login. If it's not being set right off the bat, you know there is a deeper login problem.
Also, try removing the dispatch_async(dispatch_get_main_queue()){ wrapper around your call to the segue.
It's unnecessary (logInWithUsernameInBackground already returns to the main thread) and I have a hunch that it's creating a racing condition where the local object is not being set first because Parse can't do any post-call cleanup since you're going right for the main thread.

Related

Referencing IBOutlet in another View Controller

So, I have been having some major trouble figuring this out and I have searched extensively for a solution but I surprisingly could not find one. I am attempting to create a multiple page (5, to be exact) Sign-Up for users.
I'll start off by showing you the layout of page 1 and 5 (since solving that issue will solve the issue for page 2-4):
Sign Up Page #1
Sign Up Page #5
As you may see (from the page control dots), I am using a page view controller to allow users to scroll from page to page. What I am trying to accomplish is giving the user the ability to enter their sign-up information in pages 1-5 before submitting it all at once (which can be located on page 5).
Here is the current code I am using for page #1:
class SignUpInfoViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is the current code I am using for page #5:
class TermsOfUseViewController: UIViewController {
let minPasswordCharCount = 6
#IBAction func signUpAction(_ sender: Any) {
let providedEmailAddress = SignUpInfoViewController().emailTextField.text!
let providedPassword = SignUpInfoViewController().passwordTextField.text!
let trimmedPassword = providedPassword.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if !(validEmail(enteredEmail: providedEmailAddress) && validPassword(enteredPassword: trimmedPassword)) {
invalidCredentialsAlert()
}
else {
FIRAuth.auth()?.createUser(withEmail: providedEmailAddress, password: providedPassword) { user, error in
if error == nil {
FIRAuth.auth()!.signIn(withEmail: providedEmailAddress,
password: providedPassword)
}
else {
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
}
// Email is valid if it has a standard email format
func validEmail(enteredEmail: String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
return emailPredicate.evaluate(with: enteredEmail)
}
// Password is valid if it is not empty or greater than a specified number of characters
func validPassword(enteredPassword: String) -> Bool {
if (enteredPassword != "" && enteredPassword.characters.count >= minPasswordCharCount) {
return true
}
return false
}
In the TermsOfUseViewController class, I am attempting to use the emailTextField and passwordTextField outlets from the SignUpInfoViewController, but I am receiving the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I debugged the error and saw that the emailTextField property from SignUpInfoViewController is nil and so force unwrapping it will cause the app to crash (Note: I have correctly connected the IBOutlets to the SignUpInfoViewController, so no issue there).
How can I safely transfer the usage of the IBOutlets from the SignUpInfoViewController class to the TermsOfUseViewController class without it crashing? In other words, how can I make it to where the IBOutlets are no longer nil when I reference them in the TermsOfUseViewController class?
Thank you!
That is a perfect scenario for delegate pattern
protocol SignUpProtocol: class {
func didProvideUserData(username: String ,password: String)
}
In your signup class declare a delegate: public weak var delegate:SignUpProtocol?
I am assuming when the user has provided the require info, they need to press some button to go to the next step: Thus in that button you should raise the delegate
#IBAction func nextButton(sender:UIButton) {
guard let username = usernameTextfield?.text, let password = passwordTextField?.text, else { fatalError("textfields were empty") }
if delegate != nil { // this saying when someone is listening to me, I will expose any method associated to me
delegate?.didProvideUserData(username:username, password:password) // passing the username and password from textfield
}
}
if you don't have a button, then look at property observer, where you could have some property
var didFulfill:Bool? = nil {
didSet {
if didFulfill != nil && didFulfill == true {}
// here you check if your textfields are sets then raise the delegate
}
}
set this property didFulfill = when both textfields are not empty :)
Now in your Terms class, just subscribe to that delegate
class TermsOfUseViewController: UIViewController, SignUpProtocol {
var signUpVc: SignUpInfoViewController?
override func viewDidLoad() {
super.viewDidLoad()
signUpVc = SignUpInfoViewController()
signUpVc?.delegate = self
}
func didProvideUserData(username: String, password:String) {
// there is your data
}
}
You have to take in account that you don't have all references for all UIPageViewControllers all the time. That being said, I would suggest either to keep object in UIPageViewController with updated information or using Singleton Pattern to use it to store info into it and later use it. UIPageViewController are being reused and you might have one before and one after and relying onto having them would be wrong.
You can use UIPageViewController as self.parentViewController or something like that.

Firebase uid returning nil after authentication (Swift)

In my app, as soon as it opens I check to see if the user is already authenticated in the viewdidload of the initial view. If they are already authenticated, I perform a segue to the main view. I'm even printing the uid to the log at this time and it's printing correctly.
override func viewDidLoad() {
super.viewDidLoad()
if ref.authData != nil {
let uid = ref.authData.uid
print(uid)
I then do the same later in the app to get some of the user's info when they click on their profile settings. I write the exact same code to fetch their uid, but this time the uid is returning nil and is crashing with the error
"fatal error: unexpectedly found nil while unwrapping an Optional value"
Is this a firebase or simulator issue?
Edit: This issue has only occurred twice. Otherwise, the code itself works as intended, which makes me wonder whether it is a firebase or simulator issue.
You want to use the observeAuthEventWithBlock method, which is a realtime authentication listener.
override func viewDidAppear() {
let ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com")
ref.observeAuthEventWithBlock({ authData in
if authData != nil {
// user authenticated
print(authData)
self.performSegueWithIdentifier("LoginToOtherView", sender: nil)
} else {
// No user is signed in
}
})
}
About the exact error you are encountering I am not sure, but a Swift-yer way of doing your code (and avoiding your error) would be to call:
if let uid = ref.authData.uid {
print(uid)
}
This code safely unwraps both authData and the UID.
I was having this problem and it took me hours. Then I realized that I'd just forgotten to do this:
ref = FIRDatabase.database().reference()
before
var ref: FIRDatabaseReference!

Current User isn't nil after calling PFUser.logOut() parse, swift2

I want to build a simple LogOut Button with parse but the current user is not getting nil.
PFUser.logOut()
var currentUser = PFUser.currentUser() // this should be nil but it isn't :(
print(currentUser)
I also tried:
PFUser.logOutInBackground()
var currentUser = PFUser.currentUser() // this is also not nil :(
So when I print the currentUser it is not nil like it should be. It is:
Optional(<PFUser: 0x7f8d99dd2320 , objectId: new, localId: local_58d62becf7a6f1dc> {
})
So I think the app is creating a new user?!
Try commenting out the following line of code in your AppDelegate.swift file -
PFUser.enableAutomaticUser()
enableAutomaticUser() will log in an anonymous user once you call PFUser.logOut(), and the username for an anonymous user is nil.
Check to see if we have a currentUser in the AppDelegate in the didFinishLaunchingWithOptions method
let user = PFUser.currentUser()
if user == nil
{
// present loginViewController
}
else
{
print("user is \(user?.username)") // <--- who is currently
// show any viewcontroller
}
Your logout Button should send the user back to the loginViewController after Login out of your app.
Example:
#IBAction func Logout(sender:UIButton)
{
PFUser.Logout()
let LoginViewController = storyboard.instantiateViewControllerWithIdentifier("storyBoardID")
self.presentViewController(LoginViewController, animated: true, completion: nil)
}
So From the Login View Controller, they could use their credentials to sign in back into your app, then the currentUser won't return a nil
Set return type of the function(logout) as optional type. Only optionals has the capability of holding nil value.
Here is the example that i want to tell you
func loggedOut() -> Int? {
// check you conditions here ex:
if score > 0 {
return score
} else {
return nil
}
}

Refreshing UIViewController Swift

Ok in my app i have a uiviewcontroller for the current user profile and a tableview. When the user clicks a table view cell that contains the current users followers, i refresh the view controller with the new user profile. When i add my code in the viewDidLoad everytime i hit back in the navigation bar it goes back to the last user but when i keep clicking a table view cell the app gets a memory warning and crashes. Now i tried in the viewDidAppear and doing this didnt get no crashes or memory warnings but when i hit back it goes back to the current user
Here is how i set up my code in
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
userTableView.delegate = self
userTableView.dataSource = self
let query = PFUser.query()
query.whereKey("username", equalTo: ChartsViewController.otherUser.user)
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error != nil || object == nil {
println("The getFirstObject request failed.")
} else {
//The find succeeded.
self.userProfile = object as PFUser!
}
}
And my code when the table view cell is clicked
ChartsViewController.otherUser.user = object["username"] as String
self.navigationController!.pushViewController(self.storyboard!.instantiateViewControllerWithIdentifier("OtherUserProfileViewController") as UIViewController, animated: true)

Update UILabel and TextFields with Updated Data from Parse Database

I do hope all is well. I am new to Swift. I am building a shell for 2 social app ideas of mine. I have successfully completed login, signup, logout and minimal querying using Parse. Up until now, I have been progressing, however I have reached a mental block.
I created a User Profile ViewController that queries the current user's information from the database and initially displays the results on the in the controller; First Name, Last Name, and etc.
I also created an Edit Profile ViewController that enables the user to update their profile information and logout. So far I have been able to submit the changes to the database, however I am having a hard time having the UILabels and Text Fields update to display the new values.
Some additional insight: Becuase my main view that users are redirected to after successful signin/registration is embedded in a navigation controller which means the navigation bar is inferred.
Please help me!
This is the code for my UserProfileViewController. Please let me knwo if I need to clarify.
override func viewDidLoad() {
super.viewDidLoad()
//---------------------------------------
//User Profile View - this can go between viewDidLoad and viewDidAppear functions.
//---------------------------------------
let userImageFile = PFUser.currentUser()["userProfileImage"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData!, error: NSError!) -> Void in
if error == nil {
var image = UIImage(data:imageData)
self.currentUserProfilePicture.image = image
}
}
var query = PFUser.query()
query.whereKey("objectId", equalTo: PFUser.currentUser().objectId)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
let user = PFUser.currentUser()
var firstname = PFUser.currentUser()["firstName"]! as String
var lastname = PFUser.currentUser()["lastName"]! as String
var aboutMe = PFUser.currentUser()["aboutMe"]! as String
self.firstName.text = firstName
self.lastName.text = lastname
self.aboutMe.text = aboutMe
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
}
Hi and welcome to swift.
Are you using the prepareForSegue, for sending data between ViewControllers?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourIdentifier" //you set the identifier in the storyboard
{
var vc = segue.destinationViewController as! YourDestinationViewController //the class of the destination view controller
vc.someValue = value
vc.otherValue = otherValue
vc.dataArray = array //here you set the values in the destination view controller
}
}

Resources