Swift how to sign user out when app closes using Firebase Auth - ios

This is how users navigate through my app:
Enter a phone number
Enter verification number (This allows them access to my Firestore)
Enters a username
Now they're in the main app
Pretty straightforward however the problem is if the app crashes (for whatever reason) or closes when they are entering the username, they are still technically signed in to my Firestore.
It's a problem because I programmed the app such that when the user reopens the app, they immediately go to the main screen (I created an app manager to handle this). If they close the app (or if it crashes) before they enter a username, they can go right to the main screen without a username.
I've made several different attempts in the applicationWillTerminate method to track which view controller the user is in, sign out so that when the user open the app again, my App Manager says, Auth.auth().currentUser == nil and goes to the initial view controller to start the registration process again.
var viewController : UIViewController?
var vc = UsernameViewController()
if viewController == vc {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
I expect the user who did not get a chance to create a username because the app closed or crashed to to return to the initial view controller rather than the main screen of the app.
I am currently experiencing the opposite.

Related

What is the reason to save Firebase authVerificationID?

When logging a user in or signing the user up for the first time and using phone auth it says:
Save the verification ID and restore it when your app loads. By doing
so, you can ensure that you still have a valid verification ID if your
app is terminated before the user completes the sign-in flow (for
example, while switching to the SMS app).
I'm not exactly sure what is the purpose of saving it.
0- the user opens the app and they are on the Login screen
1- the user adds their phone number (below)
2- the callback receives the authVerificationID
3- before the user is taken to the SMS screen, the app is somehow terminated
4- when the user opens the app again, because they haven't logged in yet, they are right back on the Login screen. When they enter their phone number again, they receive either a brand new authVerificationID or the same one (I'm not sure), and they are taken to the SMS screen. Either way both are valid and will get them to the SMS screen.
What does saving the authVerificationID do when no matter what, if the app is terminated, they have to add their phone number again because they will be back on the Login screen?
If the answer is "check if the authVerificationID is saved, and if it is then bring them to the SMS screen instead of the Login screen" then that is bad ux. The user might come back an hour later, when they first open the app they will see the SMS screen and be confused.
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { (verificationID, error) in
guard let verificationID = verificationID else { return }
UserDefaults.standard.set(verificationID, forKey: "authVerificationID") // how does this help me?
// A. take user to sms view controller
// B. use authVerificationID && verificationCode for sms sign-in
}
The answer is pretty much what you said - if the user leaves your app in the middle of the auth flow, you can you can resume the sign-in flow from where they left off - i.e. the SMS verification step.
Once the user leaves your app, you have no guarantees that your app will not be terminated. It depends entirely on the OS. So your user might end up in a cycle where they just can't complete the sign-up (however unlikely this case is).
As for bad UX comment - it's very subjective (and depends on your design), and also time-dependent.

WKWebview iOS (swift) : Keep session connected after app closed

I want to make an app from a PWA on iOS using a simple WKWebView. I've managed to implement it in a simple ViewController and it is working fine.
Problem : when i login and then quit the app, the session is lost and i have to login again.
Question : Is it possible to keep the session informations after the user quit the app ?
Thanks in advance for your answers !
You need to look into persisting your session data. For WKWebView, all of that is part of WKProcessPool. So when your app goes into the background, you need to look into persisting the WKProcessPool session, and when you use your web view, always use that same WKProcessPool instance.
Here's an answer that may help you save that data to your UserDefaults.
https://stackoverflow.com/a/52109021/2658489
I think you should implement login API, for authorization and Login UI on Native App, and then you can have dashboard items using single WKWebView controller.
When a user logged in with the native page - loginViewController, you should store user name, password (maybe encrypted) in user preferences, e,g.
UserDefaults.standard.set(userName, forKey: keyUserName)
UserDefaults.standard.set(userPassword, forKey: keyPassword)
UserDefaults.standard.synchronize()
Next time (after quit App) when you back you can check for auto login in AppDelegate - didFinishLaunchingWithOptions
e.g.
if let username = UserDefaults.standard.value(forKey: keyUserName) as? String,
password = UserDefaults.standard.value(forKey: keyPassword) as? String
{
/// DO AUTOLOGIN .. CALL API AND LAND TO DASHBOARD PAGE (WKWebView)..
}

SKStoreReviewController.requestReview() not working in Live App

This is my code for Requesting Review :
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}
else{
print("Review is not available with in the app")
}
In Development Mode it is working properly & I am able to get PopUp like this:
But In Live app downloaded from appstore, App isnot showing this ratings Popup and nothing happens if user taps out on Ratings Button.
From the documentation:
Although you should call this method when it makes sense in the user experience flow of your app, the actual display of a rating/review request view is governed by App Store policy. Because this method may or may not present an alert, it's not appropriate to call it in response to a button tap or other user action.
(Highlight mine)
If you have a Ratings Button like you said in your question, you should not expect it to show the prompt.
The prompt will only show up if:
The user hasn't disabled Review Prompts in Settings.
The prompt has been shown to the user 3 times or less in a year.
If you must request a review upon user interaction, you must direct your users to the App Store page of your app instead, using code like this (taken from Requesting App Store Reviews Sample Code):
#IBAction func requestReviewManually() {
// Note: Replace the XXXXXXXXXX below with the App Store ID for your app
// You can find the App Store ID in your app's product URL
guard let writeReviewURL = URL(string: "https://itunes.apple.com/app/idXXXXXXXXXX?action=write-review")
else { fatalError("Expected a valid URL") }
UIApplication.shared.open(writeReviewURL, options: [:], completionHandler: nil)
}

GameCenter - Login user if they already entered their credentials

In my game I don't want the GameCenter login popup appearing automatically when the app launches. So instead I have a GameCenter button that the user can tap to login with. When they press the button, the login screen appears.
However, it seems like when you launch the app again after logging in, the user still isn't "logged in". They still have to press the button again and then a little banner appears saying "Welcome back, User!". Is there a way to automatically relogin the user without them having to press the button each time? I already entered my credentials, why do I have to authenticate again?
Here is my code, when the user presses the button:
self.authenticatePlayer()
And here's the authenticate method:
func authenticatePlayer() {
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil {
self.view?.window?.rootViewController?.presentViewController(view!, animated: true, completion: nil)
} else {
}
}
}
I need something that can welcome back the user if they already logged in, but NOT ask them to login if they haven't logged in already.
To understand the behavior you're seeing, we need to look at how the authentication process works.
It starts when you set the authentication handler. This is the signal that tells your app to try and talk to Game Center. The authentication handler's completion block has three possible conditions:
Error: something went wrong
Receives a view controller: the login view controller tells you the player isn't logged in
Receives nil view controller: the lack of an error + lack of a view controller tells you the player was already logged in.
Although IOS may be aware of your login state (or attempting to fake your login state using cached info), your app loses that context when you exit. When you startup again, there's been no attempt to set the authentication handler, thus no attempt to verify authentication status until your user presses the button, thus your app doesn't know whether the user is logged in or not.
I think the following approach will get pretty close to what you're looking for:
set the authentication handler and initiate the authentication as early as possible in your first viewController's viewDidLoad. Do this as early as possible in your start up sequence.
If the user isn't authenticated, you will receive a login view controller. Don't present it. Instead, save it. Don't present it unless/until the user presses the button.
If the user is already logged in, they will see the welcome back message as soon as the game starts, and you'll be able to proceed since the user is still logged in.

In which method should I check for NSUserDefaults?

In my iOS app I made a Login page as the entry point of the app (using storyboard)
However I don't want the user to see the login page each time he uses the app, so I thought of launching the Home page of the app if the user already performed a login in the past.
For that I started to save the login act with NSUserDefaults but I do not know in which of the LoginViewController method I should check for it? Also, is this way to feature "auto logging" a good practice?
Best place is in application:didFinishLaunchingWithOptions
if ([[NSUserDefaults standardUserDefaults] integerForKey:#"UserID"]==0) {
//no user login go back to login page
}
else{
//go in main screen of you application as user already login "root" is storyboard ID of main screen
self.window.rootViewController=[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"root"];
}

Resources