How to check if a user upgraded using parse? - ios

My app has a login and signup. Once the user is logged in he can then choose to upgrade the account. When the account is upgraded, a new class in parse is created called "Upgrade". Here it has a bunch of subclasses with stored information. Then once the user is upgraded it brings him to a special page that only upgraded users have access to. But how can I check on login if the user is upgraded, and if he is, automatically bring him to the special page.
In my parse, I have the User information stored with subclasses "Username" and "Password". Then in a separate class I have the upgrade information stores with subclasses "Address", "Phone Number", and I have a linker to link back to the user who created it.
my current code for login is:
#IBAction func loginButton(sender:AnyObject) {
var username = self.usernameTextField.text
var password = self.passwordTextField.text
if(password.utfCount <5) {
var alert = UIAlertView(title:"Invalid", message: "Password must be greater than 5", delegate: self, cancelButtonTitle:"OK")
}
else {
PFUser.logInWithUsernameInBackground(username, password: password, block:{(user, error) -> Void in
if ((user != nil) {
self.performSegueWithIdentifier("LoginSegue", sender: nil)
This is the basic code but it does not check to see if the user is upgraded.
I tried:
if(PFUser.currentUser() == PFQuery(className:"Upgrade")) {
self.performSegueWithIdentifier("UpgradedSegue")
But obivously this didnt work due to the current user not equaling that class.
What kind of code could I user to check if the user made a Upgrade class within parse?
I have tried messing around with fetchinbackground code and enter code hereobjectinbackground but I can't seem to make those work.

I don't know the Swift very well, so sorry if there are errors, but try something like:
var query = PFQuery(className:"Upgrade")
query.whereKey("user", equalTo:currentUser.objectId)
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject!, error: NSError!) -> Void in
if error != nil
println("The getFirstObject request failed.")
} else if object == nil{
//No Upgrade object with that user's ID
} else {
// The find succeeded, user has created an Upgrade object
println("Successfully retrieved the object.")
}
}
You could also set up your code to store the pointer to the Upgrade object on the user, rather than the other way around. You could set a bool value on the user to see if they have upgraded. If it is true, then take them to the upgrade screen, or fetch the Upgrade object, whatever you need first.

Related

Catch certain Firebase Error in iOS not working

Updated question
I am trying to manually check if the user is has to be reauthenticated or not. This is what I've come up with:
//MARK: updateEmail
static func updateEmail(email: String, finished: #escaping (_ done: Bool, _ hasToReauthenticate: Bool) -> Void) {
let currentUser = Auth.auth().currentUser
currentUser?.updateEmail(to: email) { err in
if err != nil {
if let errCode = AuthErrorCode(rawValue: err!._code) {
switch errCode {
case .userTokenExpired:
print("expired")
finished(true, true)
break
default:
Utilities.showErrorPopUp(labelContent: "Fehler", description: err!.localizedDescription)
finished(false, false)
}
}
} else {
finished(true, false)
}
}
}
But this is never going through the .userTokenExpired case even when it should.. What am I missing here ?
There is no API in Firebase Authentication that returns when the user has last authenticated, or whether that was recently. The only built-in functionality is that Firebase automatically checks for recent authentication for certain sensitive operations, but that seems to be of no use to you here.
But since your application is making API calls when the user authenticates, you can also record the time when they do so, and then check whether that was recent enough for your use-case.
If you need to check if user is authenicated - is same as reauthenication. Firebase will do their work to do some lower levels like tokens, etc. We don't have to worry about it.
guard let currentUser = Auth.auth().currentUser else {
//authenicate the user.
}
if you want to update the email address in user, the logic should be
check if the user is not nil, then update the email address.
If it is nil, then log in (anonymous or regular workflow to sign in), then update the email address.
I use this similar logic to check if the user is signed in, then do something. Otherwise, sign in as anonymous, then do same something.
The issue was quite simple: I caught the wrong error:
The error I have to catch in my case is .requiresRecentLogin . With that, everything is working fine.

How to set up Firebase iOS authentication email

Having looked through lots of previous questions and looking on the Firebase website documentation, it keeps leading me back to the snippet of code I need in my VC, BUT not how to actually set it up?.
Firstly in the email address verification setup on Firebase
I've by mistake put my personal email address as the 'reply to' email - do I put my personal (not business) email in there/how would I change it? Apologies for any over the top censoring (not sure what is private and not)
Secondly in my SignUpViewController what do I put as the URL String and what do I put as my IOSBundleID? Many thanks!
To change the email go to Authentication and press templates. There you have some options for your mail.
Press the pen beside noreply#yourfirebase.firebaseapp.com.
There you will have a replay to line and you can change all those settings
This is all you need to register a new user :
Auth.auth().createUser(withEmail: emailText.text!, password: passwordText.text!) {
(user, error) in
if error != nil {
print(error.localizedDescripton)
}else {
print("registration successful")
}
}
To send confirmation email to user make a call after user is created and use this method :
func sendConfirmationEmail() {
// Here you check if user exist
if self.authUser != nil && !self.authUser!.isEmailVerified {
self.authUser!.sendEmailVerification(completion: { (error) in
// Send the email
})
}
else {
// ERROR
}
}
You could now call the second method after user been created and the user will get an email

Update Parse ACL on queried object

In my iOS app I am making a PFObject and saving it to Parse. Later, a user's account is created (which didn't exist before), and tries to modify it but can't because the PFObjects's ACL wasn't set to allow that user to have permission. How can I modify the ACL of an existing object in Parse to allow this user to have access? I do not want to allow public write access.
The following code prints Success! if given the right code query parameter, but when I check the ACL in Parse it has not been updated at all.
let query = PFQuery(className: "Bike")
query.whereKey("bikeID", equalTo: code)
query.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
guard let obj = objects?[0], error == nil else {
print("Error")
return
}
obj.acl?.setWriteAccess(true, for: PFUser.current()!)
obj.saveInBackground { (success: Bool, error: Error?) in
if error != nil {
print(error!.localizedDescription)
}
else {
print("Success!")
}
}
}
This post seems to suggest that the ACL cannot be changed through my app's Swift code.
If you know the object you want to grant access to up-front you can change it's ACL in the CloudCode afterSave hook of the User class: in afterSave, test whether the user was just created (to avoid redoing this work for subsequent save requests), then look up the object and set the access rights using the master key.

Parse + Swift + Anonymous

In an effort to create the easiest user experience possible, I am on a mission to accept a user as an anonymous user using Parse + Swift. I had thought to use the Anonymous user functions in Parse to accomplish that. As a result, I created the following code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.setupParse()
// self.setupAppAppearance()
This first section is to create a user and see if at this point in the process - I have a nil objectId (typically true for the user when first they attempt to open the application).
var player = PFUser.currentUser()
if player?.objectId == nil {
}
else
{
println(player!.objectId)
}
If I have an objectId (indicating that I've been down this road before and saved an anonymous user object) - throw that to the console so I can see what it is and check it in the Parse user object. Cool - good so far.
Next - Check to see if the Object is nil again - this time to decide whether or not to attempt to perform an anonymous login - there's not a thing to use to generate an anonymous user other than this anonymous login action.
if player?.objectId == nil {
PFAnonymousUtils.logInWithBlock({
(success, error) -> Void in
if (error != nil)
{
println("Anonymous login failed.")
}
else
{
println("Anonymous login succeeded.")
If anonymous Login succeeded (still considering doing a network available check before trying to run these bits...but assuming network is valid) save a Bool to "isAnonymous" on the server to make sure that we have identified this user as anonymous - I may want that information later, so it seemed worth throwing this action.
Question 1: Do I need to re-query PFUser.currentUser() (known as player) to make sure that I have a valid anon user object that is connected to the server, or will the player object that I allocated earlier recognize that I've logged in and thereby recognize that I can throw other info into the associated record online? I think this is working as is - but I've been getting weird session token errors:
[Error]: invalid session token (Code: 209, Version: 1.7.5)
player!["isAnonymous"] = true as AnyObject
player!.saveInBackgroundWithBlock {
(success, error) -> Void in
if (error != nil)
{
println("error updating user record with isAnonymous true")
}
else
{
println("successfully updated user record with isAnonymous true")
}
}
}
})
}
else
{
}
return true
}
func setupParse()
{
Parse.setApplicationId("dW1UugqmsKkQCoqlKR3hX8dISjvOuApcffGAWR2a", clientKey: "BtXxjTjBRZVnCZbJODhd3UBUU8zuoPU1HBckXh4t")
enableAutomaticUserCreateInParse()
This next bit is just about trying to figure out some way to deal with those token problems. No idea whether it's doing me any good at all or not. It said to turn this on right after instantiating the Parse connection.
PFUser.enableRevocableSessionInBackgroundWithBlock({
(error) -> Void in
if (error != nil) {
println(error?.localizedDescription)
}
})
Next - just throwing around objects because I keep struggling with being connected and storing stuff or not being connected or losing session tokens. So - til I get this worked out - I'm creating more test objects than I can shake a stick at.
var testObject = PFObject(className: "TestObject")
testObject["foo"] = "barnone"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved.")
}
}
Question2: it appears to me that PFUser.enableAutomaticUser() while very handy - causes some headaches when trying to figure out whether I'm logged in/online/whatever. Anyone have any solid experience with this and able to guide me on how you'd check whether you were connected or not - I need to know that later to be able to decide whether to save more things to the user object or not.
func enableAutomaticUserCreateInParse() {
if PFUser.currentUser()?.objectId == nil
{
myHumanGamePlayer.playerDisplayName = "Anonymous Player"
PFUser.enableAutomaticUser()
}
}
Anyone out there who's an expert on using anonymous users in Parse with Swift, let's get in touch and post a tutorial - because this has cost me more hours than I'd like to think about.
Thank you!
Xylus
For player!["isAnonymous"] = true as AnyObject, don't save it as any object. Save it as a bool and look at your parse to see if it's a bool object. Try querying for current user in a different view controller and print to the command line. I hope this helped

Add column to PFUser AFTER signup? Parse, Swift

I would like my user to add/edit details about their profile after they register with my app.
#IBAction func doneEditting(sender: AnyObject) {
self.completeEdit()
}
func completeEdit() {
var user = PFUser()
user["location"] = locationTextField.text
user.saveInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if let error = error {
let errorString = error.userInfo?["error"] as? NSString
println("failed")
} else {
self.performSegueWithIdentifier("Editted", sender: nil)
}
}
}
the breakpoint stops right at user.saveInBackgroundWithBlock. No of the docs show how to append new columns after the signup.
Thanks!
You are mentioning that the user should be able to edit their profile after they have registered. When registering a user with Parse using signUpInBackgroundWithBlock, then the Parse SDK will automatically create a PFUser for you.
In your provided code you are creating and saving a completely new PFUser instead of getting the one which is currently logged in. If you are not using the PFUser which is logged in, then you will get the following error at user.saveInBackgroundWithBlock (which you are also mentioning in your post):
User cannot be saved unless they are already signed up. Call signUp first
To fix this, you will need to change:
var user = PFUser()
To the following:
var user = PFUser.currentUser()!
The rest of your code (for example user["location"] = locationTextField.text) works fine and will dynamically/lazily add a new column to your User database (which is what you want).
Parse allows you to add columns to a class lazily, meaning that you can add a field to your PFObject and if it is not present in your Parse class, Parse will add that column for you.
Here's example how you would add a column via code:
// Add the new field to your object
yourObject["yourColumnName"] = yourValue
yourObject.saveInBackground()
You'll notice that Parse will create a new column named yourColumnName on their web portal.
reference from HERE.

Resources