use of restorePreviousSignIn() in googleSign in iOS - ios

Google migration doc here says that
// Old:
guard let signIn = GIDSignIn.sharedInstance() else { return }
if (signIn.hasAuthInKeychain()) {
signIn.signInSilently()
}
// New:
guard let signIn = GIDSignIn.sharedInstance() else { return }
if (signIn.hasPreviousSignIn()) {
signIn.restorePreviousSignIn()
// If you ever changed the client ID you use for Google Sign-in, or
// requested a different set of scopes, then also confirm that they
// have the values you expect before proceeding.
if (signIn.currentUser.authentication.clientID != YOUR_CLIENT_ID
// TODO: Implement hasYourRequiredScopes
|| !hasYourRequiredScopes(signIn.currentUser.grantedScopes)) {
signIn.signOut()
}
}
As I had tried with device user still get redirect to account.google.com and have to choose the account right? so what is use of restorePreviousSignIn(). How it benefit to user? Thanks in advance.

Do you set the GIDSignInDelegate (GIDSignIn.sharedInstance().delegate) before calling restorePreviousSignIn()?
As the documentation of restorePreviousSignIn() says:
The delegate will be called at the end of this process indicating success or failure. The current values of GIDSignIn's configuration properties will not impact the restored user.
Btw. I'm using the restorePreviousSignIn() without any trouble.
// Sorry for asking in answer, I don't have enough reputation to comment on you question.

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.

"provideCredentialWithoutUserInteractionForIdentity:" is not working

I have an app that autofills the password on iOS 12. Now I want to implement this function:
- (void)provideCredentialWithoutUserInteractionForIdentity:(ASPasswordCredentialIdentity *)credentialIdentity;
But I cant get it to work like it should.
AutoFill is enabled in settings and also enabled for my app.
I've read the documentation but this doens't help me. https://developer.apple.com/documentation/authenticationservices/ascredentialproviderviewcontroller/2977554-providecredentialwithoutuserinte?language=objc
I've tried calling this function from viewDidLoad, prepareCredentialListForServiceIdentifiers,.. but this is stupid and definitely won't work.
- (void)provideCredentialWithoutUserInteractionForIdentity:(ASPasswordCredentialIdentity *)credentialIdentity {
ASPasswordCredential *credential = [[ASPasswordCredential alloc] initWithUser:#"theUsername" password:#"thePassword"];
[self.extensionContext completeRequestWithSelectedCredential:credential completionHandler:nil];
}
The function should show the credentials above the keyboard, but this doesn't happen. It just shows the default "Passwords" button.
Make sure you have some ASPasswordCredentialIdentitys for your domain/url in ASCredentialIdentityStore. (These are records with some unique recordIdentifier, that doesn't hold password but hold data that can help you decide what password you should take from some secure storage of your choice.)
When you open a website, iOS looks up the ASCredentialIdentityStore, and shows a big button for quick login if there's a matching record. If you hit the button - this is only when provideCredentialWithoutUserInteraction callback is executed. Your task is to work with ASPasswordCredentialIdentity passed as an argument (it has recordIdentifier field) and find matching password for it (in your database/keychain/etc.) When you have password - you create ASPasswordCredential and pass it to self.extensionContext.completeRequest. Also make sure to call extensionContext.cancelRequest in case of any errors.
here's my example
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
let databaseIsUnlocked = false
if (databaseIsUnlocked) {
// this function queries my custom keychain records by recordIdentifier (Generic Passwords) to find matching password
getItemForRecordId(identifier: credentialIdentity.recordIdentifier) { password, err in
guard password != nil else {
print("password was nil")
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
return;
}
let passwordCredential = ASPasswordCredential(user: credentialIdentity.user, password: password as! String);
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil);
};
} else {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
}
}

iOS/Swift/Firebase Authentication: Help Needed on Accessing "isNewUser"

In my iOS/Swift/Firebase app, I am trying to access the "isNewUser" parameter after a user successfully signs in via email/password so that I can pop a window to compel them to reset their password from the temporary one initially assigned upon user creation.
Any insights would be appreciated. Thanks.
The .isNewUser Bool is available from the FirebaseAuth AdditionalUserInfo class.
Here is the link. In order to utilize this code, please see a demo sign in function I wrote below.
Auth.auth().signIn(with: credential) { (result, error) in
if let error = error {
print("Error: \(error)");
return;
}
// Fetch the user's info
guard let uid = result?.user.uid else {return}
// Safely unwrap the boolean value rather than forcing it with "!" which could crash your app if a nil value is found
guard let newUserStatus = result?.additionalUserInfo?.isNewUser else {return}
// Test the value
print("\nIs new user? \(newUserStatus)\n")
if newUserStatus == true {
// Provide your alert prompt
}
else{
// Transition view to continue into the app
}
}

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

Access "My Info" from iOS app [duplicate]

Outside of asking the user to input their name, is there any way to get it off the device?
I tried this library, which attempts to extract the name from [UIDevice currentDevice] name], but that doesn't work in a lot of situations:
https://github.com/tiboll/TLLNameFromDevice
Is the user's name present in the phonebook or anywhere else that we have access to in iOS 6?
Well you could go through all the contacts in the AddressBook and see if any of them are marked with the owner flag.
Just be aware that doing this will popup the "this app wants access to the address book" message. Also Apple isn't very keen on these kind of things. In the app review guide it is specified that an app can not use personal information without the user's permission.
You could use Square's solution:
Get the device's name (e.g. "John Smith's iPhone").
Go through the contacts on the phone and look for a contact named "John Smith".
JBDeviceOwner and ABGetMe will both do this for you.
You could use CloudKit. Following a snippet in Swift (ignoring errors):
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler(
{
(recordID, error) in
container.requestApplicationPermission(
.PermissionUserDiscoverability,
{
(status, error2) in
if (status == CKApplicationPermissionStatus.Granted)
{
container.discoverUserInfoWithUserRecordID(
recordID,
completionHandler:
{
(info, error3) in
println("\(info.firstName) \(info.lastName)")
}
)
}
}
)
}
)
The above code was based on the code at http://www.snip2code.com/Snippet/109633/CloudKit-User-Info
to save folks time. in swift4:
let container = CKContainer.default()
container.fetchUserRecordID(
completionHandler: {
(recordID, error) in
guard let recordID = recordID else {
return
}
container.requestApplicationPermission(
.userDiscoverability,
completionHandler: {
(status, error2) in
if (status == CKContainer_Application_PermissionStatus.granted)
{
if #available(iOS 10.0, *) {
container.discoverUserIdentity(withUserRecordID:
recordID,
completionHandler:
{
(info, error3) in
guard let info = info else {
return
}
print("\(info.firstName) \(info.lastName)")
}
)
}
}
}
)
}
)
however: CKUserIdentity no longer exposes either first or last name
So this answer no longer works.
You can use:
NSLog(#"user == %#",[[[NSHost currentHost] names] objectAtIndex:0]);
I did receive compiler warnings that the methods +currentHost and -names were not found. Given the warning, I’m not sure of Apple’s intention to make this available (or not) as a publicly accessible API, however, everything seemed to work as expected without the need to include any additional header files or linking in additional libraries/frameworks.
Edit 1:
You may also take a look at this Link
Edit 2:
If you have integrated your app with Facebook you can easily retrieve the user info, see Facebook Fetch User Data
For SWIFT you can use
NSUserName() returns the logon name of the current user.
func NSUserName() -> String

Resources