Google login user profile not updated to Firebase - swift

I am new to Xcode and I am working on my Google sign-in project. After successfully logging in with google, I cannot find the user profile in firebase. I think I am missing the code for uploading user credentials to firebase. I can barely understand the documentation and I don't know what to do. By the way, how can I navigate to another view controller after signing in? I tried lots of ways but none of them work. Any help is appreciated.
My AppDelegate code:
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance()?.clientID = "//I have it in my actual code"
GIDSignIn.sharedInstance()?.delegate = self
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error)
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken,
accessToken: authentication.accessToken)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance().handle(url)
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
The code of the log-in view controller
import UIKit
import GoogleSignIn
class WelcomeViewController: UIViewController {
#IBOutlet weak var gooleButton: GIDSignInButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
GIDSignIn.sharedInstance()?.presentingViewController = self
gooleButton.style = .wide
gooleButton.colorScheme = .dark
}
}

From what I see your code signs the user in with Google to get the credentials, but then doesn't call signIn(with: ) which is needed in order to sign in to Firebase Authentication.
Have a look at Step 3: authenticate with Firebase. While the code there looks a bit intimidating, that's mostly because it handled many different cases. At its minimum you can sign in to Firebase with your credentials by doing:
Auth.auth().signIn(with: credential) { authResult, error in
if let error = error {
let authError = error as NSError
print(error.localizedDescription)
return
}
// User is signed in
// ...
}

Related

Unable to navigate to other view controller after Google sign-in

How can I go to another view controller after successfully signing with google? I've tried "self.inputViewController?.performSegue(withIdentifier: "goToMain", sender: self)" in my App Delegate but not getting any respond. Am I supposed to add the method in App Delegate or the view controller with the sign-in button?
App Delegate
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance()?.clientID = "my_client_id"
GIDSignIn.sharedInstance()?.delegate = self
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error)
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { authResult, error in
if let error = error {
let authError = error as NSError
print(authError.localizedDescription)
return
}
self.inputViewController?.performSegue(withIdentifier: "goToMain", sender: self)
}
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance().handle(url)
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
Are you sure about the inputViewController is not nil? This may a reason for not working.
You can add a function to test this, or you can use the print function to check, or, you can use debug point to check inputViewController is nil or not.
I'd like to add comment but I don't have enough reputation point :).
Good luck.

Handling Phone Authentication using FirebaseUI and SwiftUI

I am attempting to use the FirebaseUI pre-built phone authentication screen to request a verification code. I am using SwiftUI, so a lot of the Firebase documentation is outdated, but I've gotten most of it working. To be explicit: I can open the phone Auth UI, type in my phone number, and input the verification code.
Where I'm currently having an issue is the part in which they tell you to receive the user info by using:
func authUI(_ authUI: FUIAuth, didSignInWith user: FIRUser?, error: Error?) {
// handle user and error as necessary
}
It says to put this in your FUIAuthDelegate. However I do not have an FUIAuthDelegate, and do not know if this is outdated, or something I'm lacking.
How I currently have it set up, is using some of Peter Friese's answer regarding the new SwiftUI 2.0 Lifecycle.
Where am I supposed to receive the Firebase User information in this new lifecycle? I have tried to make my AppDelegate an FUIAuthDelegate and add the "authUI" function into it, and that didn't work. I have also tried to add an "authUI" function to my LoginView struct, and that didn't work either.
Please see below for a more in-depth look at my code:
///testApp.swift
import SwiftUI
import Firebase
import FirebaseUI
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
print("App Starting Up...")
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Registered for remote notifications...")
Auth.auth().setAPNSToken(deviceToken, type: .sandbox)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if Auth.auth().canHandle(url){
return true
}
print("URL Error - oops")
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping(UIBackgroundFetchResult) -> Void) {
if Auth.auth().canHandleNotification(userInfo) {
print("Handling Notification")
completionHandler(.noData)
return
}
return
}
}
#main
struct testApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
AnyView(LoginView())
}
}
}
And my LoginView:
///LoginView.swift
import SwiftUI
import Firebase
import FirebaseAuth
import FirebaseUI
struct LoginView: View {
#State var verified: Int? = nil
var body: some View {
NavigationView {
NavigationLink(destination: LoggedInView(), tag: 1, selection: $verified) {
Button(action: {
login()
}
}
}
}
func login() {
let authUI = FUIAuth.defaultAuthUI()!
let providers: [FUIAuthProvider] = [FUIPhoneAuth(authUI: FUIAuth.defaultAuthUI()!)]
authUI.providers = providers
let authViewController = authUI.authViewController()
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {return}
guard let rootViewController = windowScene.windoes.first?.rootViewController else {return}
Auth.auth().settings?.isAppVerificationDisabledForTesting = true
rootViewController.present(authViewController, animated: true, completion: nil)
}
}
I am currently testing on the xcode simulator, and using a dummy account from my firebase console.
If you have any questions, please feel free to ask and I will do my best to clarify. Any help would be much appreciated. Thanks!

What is the best way to sign out and sign back into google in an iOS app?

I am currently signing my user out like this:
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - signed in
GIDSignIn.sharedInstance()?.signOut()
print(GIDSignIn.sharedInstance()?.currentUser != nil) // false - signed out
Here is my is My AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
// Intializing Google Sign In...
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().scopes = []
// FirebaseApp.app()?.options.clientID = "674329356626-ev6hn62oiqcj0o228nt85vv2bgroa45j.apps.googleusercontent.com"
return true
}
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any])
-> Bool {
return GIDSignIn.sharedInstance().handle(url)
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if error != nil{
print(error.localizedDescription)
return
}
let credential = GoogleAuthProvider.credential(withIDToken: user.authentication.idToken, accessToken: user.authentication.accessToken)
// Logging to Firebase...
Auth.auth().signIn(with: credential) { (res, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
// User Logged In Successfully...
// Sending Notification To UI...
NotificationCenter.default.post(name: NSNotification.Name("SIGNIN"), object: nil)
// Printing Email ID
print(res?.user.email)
}
// Check for sign in error
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("\(error.localizedDescription)")
}
return
}
// Post notification after user successfully sign in
NotificationCenter.default.post(name: .signInGoogleCompleted, object: nil)
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
Sign in code:
GIDSignIn.sharedInstance()?.signIn()
Once the user is signed in, what is the best way for me to sign them out and prompt for them to sign in again? Please note that I am using the GoogleSignIn api.
Also note that I setup my google sign in functionality from here https://developers.google.com/identity/sign-in/ios/start?ver=swift
What seems to be happening in my app is the when my sign out function executes the current user is set to nil. However, when the user is signed out I can still access the data from the signed out user. I want the user to be able to sign out of their current account and sign into a new account for which I can access data from their newly signed in account.
My app should not retain data from the previous logged out user.
GIDSignIn.sharedInstance()?.signOut()
I also setup my google sign with firebase which seemed to help

How to display the name of a user logged in with Google Sign In and Firebase in another UIViewController

I have a question.
Firebase DATABASE:
{
"Users" : {
"109015298583739078046" : {
"Name" : "Manuel Schiavon"
}
}
}
I want to display the name "Manuel Schiavon" in another UIViewController but I don't know how. Now the name it's Manuel Schiavon but it's only an example so if another user logs into my app (with Google Sign in) should see his name on the screen not the mine.
THIS IS MY APP DELEGATE.SWIFT
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance().handle(url,
sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplicationOpenURLOptionsKey.annotation])
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
return GIDSignIn.sharedInstance().handle(url,
sourceApplication: sourceApplication,
annotation: annotation)
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
// ...
if error != nil {
// ...
return
}
print("L'utente è entrato in Google")
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken
)
// ...
Auth.auth().signInAndRetrieveData(with: credential) { (authResult, error) in
if error != nil {
// ...
return
}
// User is signed in
// ...
let userID: String = user.userID
let userName: String = user.profile.name
Database.database().reference().child("Users").child(userID).setValue(["Name": userName])//Salva il nome in Firebase
self.window?.rootViewController?.performSegue(withIdentifier: "HomeSegue", sender: self) //Per andare al secondo screen, "HomeSegue è il nome del collegamento tra il UIViewController 1 e 2.
}
print ("L'utente è entrato in Firebase")
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
// Perform any operations when the user disconnects from app here.
// ...
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
THIS IS MY VIEWCONTROLLER.SWIFT
import UIKit
import Firebase
import GoogleSignIn
class ViewController: UIViewController, GIDSignInUIDelegate {
#IBOutlet weak var LB_username: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
GIDSignIn.sharedInstance().uiDelegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
THIS IS MY STORYBOARD
Click here to see the storyboard
pls help me! Thanks!
Edit 2 - Now I see the code
Add this in viewDidLoad of your ViewController and forget what we said so far.
if Auth.auth().currentUser != nil {
if let name = Auth.auth().currentUser?.displayName {
LB_username.text = name
}
}
Edit - "where and how should I declare userName? Short answer"
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
var userId: String?
var userName: String?
//...
"I don't know how to handle optionals" - In this case, do this:
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
var userId = String()
var userName = String()
//...
Old Answer
Your answer is not clear, so I try to guess what you want to achieve:
Assuming you already have your name stored in userName, as I can see here:
Database.database().reference().child("Users").child(userID).setValue(["Name": userName])
what you want to do is to pass this string in your "HomeSegue" segue.
To do this you need to implement prepare for segue method
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621490-prepare
Here's an example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "HomeSegue" {
let destinationVC = segue.destination as! MySecondViewController
destinationVC.myLabel.text = userName
}
}
Note:
- userName needs to be global
- myLabel is a property of MySecondViewController

How can I get the Google Sign in Button to work in Xcode 9.3?

I am having trouble getting my google sign in button working in Xcode 9.3-beta.
Here is the code from my app delegate:
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Initialize Firebase
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
// Override point for customization after application launch.
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
// ...
if error != nil {
// ...
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
// ...
}
#available(iOS 9.3, *)
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any])
-> Bool {
return GIDSignIn.sharedInstance().handle(url, sourceApplication:options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String, annotation: [:])
}
}
And here is the code for my register View Controller:
import UIKit
import Firebase
import GoogleSignIn
class registerViewController: UIViewController, GIDSignInUIDelegate {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var signInButton: GIDSignInButton!
//Email_password combination
#IBAction func complete(_ sender: Any) {
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordTextField.text!) {
(user, error) in
if error != nil {
print(error!)
} else {
//success
let welcomeMessage = "Signed In"
let alert = UIAlertController(title: welcomeMessage, message: "Welcome", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), style: .`default`, handler: { _ in
NSLog("The \"OK\" alert occured.")
}))
self.present(alert, animated: true, completion: nil)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signIn()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//GID sign in method
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
// ...
if error != nil {
// ...
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (user, error) in
if error != nil {
// ...
return
}
// User is signed in
// ...
}
// ...
}
When I run this app, I can navigate successfully to my register view controller and my view for the GID sign in button loads successfully. however, nothing happens when I click the button. I've been working with this for hours and haven't gotten anywhere.
In the debug console, no errors come up. but I do get at the top:
"Unknown class _TtC4LEAF14ViewController in Interface Builder file."
I also get
"Status bar could not find cached time string image. Rendering in-process."
I can't figure out what either of these are referring to. I made sure to use the correct URL types, and have incorporated the pod files correctly. My email-password combination works as well.
I had similar problem with Xcode 9.3 (official version) and Google login. I tested with Xcode 9.4 beta and everything works again.