I have been following this tutorial on how to make a facebook login with swift. http://letsbuildthatapp.com/2016/03/26/swift-facebook-login-sdk-and-getting-users-email/
But, when i dismiss the app by double clicking on home button and sliding up the application I see my login screen with one difference. Button's text has changed to logout.
What i want to do is automatically skipping that login page and performing this segue: self.performSegueWithIdentifier("redirectAfterLogin", sender: self)
I come across this article yet I couldn't fix the issue by myself and now I'm little bit confused.
How to check if user is logged in with FBSDKLoginKit in iOS?
Below you can see my AppDelegate.swift and LoginVC.swift files. How can I fix this?
AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CNPPopupControllerDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation)
}
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 throttle down OpenGL ES frame rates. 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 inactive 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:.
}
}
LoginVC.swift
import UIKit
import KASlideShow
class LoginVC: UIViewController, KASlideShowDelegate, FBSDKLoginButtonDelegate {
let loginButton: FBSDKLoginButton = {
let button = FBSDKLoginButton()
button.readPermissions = ["email"]
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
// do stuff here
view.addSubview(loginButton)
loginButton.center = view.center
loginButton.readPermissions = ["public_profile", "email", "user_friends"]
loginButton.delegate = self // Remember to set the delegate of the loginButton
if let token = FBSDKAccessToken.currentAccessToken() {
fetchProfile()
}
if FBSDKAccessToken.currentAccessToken() != nil {
print("1")
self.performSegueWithIdentifier("redirectAfterLogin", sender: self)
}
}
func fetchProfile(){
print("fetching profile..")
let params = ["fields": "email, first_name, last_name, picture.type(large)"]
FBSDKGraphRequest(graphPath: "me", parameters: params).startWithCompletionHandler{ (connection, result, var error) -> Void in
if error != nil {
print (error)
return
}
if let email = result["email"] as? String {
print(email)
NSUserDefaults.standardUserDefaults().setObject(email, forKey: "email")
}
if let first_name = result["first_name"] as? String {
print(first_name)
NSUserDefaults.standardUserDefaults().setObject(first_name, forKey: "first_name")
}
if let last_name = result["last_name"] as? String {
print(last_name)
NSUserDefaults.standardUserDefaults().setObject(last_name, forKey: "last_name")
}
if let picture = result["picture"] as? NSDictionary, data=picture["data"] as? NSDictionary, url=data["url"] as? String {
print(url)
NSUserDefaults.standardUserDefaults().setObject(url, forKey: "url")
}
NSUserDefaults.standardUserDefaults().synchronize()
print(result) // this prints whole FBSDKGraphRequest API response
}
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
print("login completed")
fetchProfile()
self.performSegueWithIdentifier("redirectAfterLogin", sender: self)
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
}
func loginButtonWillLogin(loginButton: FBSDKLoginButton!) -> Bool {
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
What you'll need to do is in appDelegate on
application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:
if let root = UIApplication.sharedApplication().windows[0].rootViewController as? UINavigationController {
if CONDITION {
root.viewControllers = [USER_HOMEPAGE]
} else {
root.viewControllers = [USER_LOGIN]
}
}
Related
I have read the google docs to seek for an answer, I also looked on some similar posts but none of these helped me solve my problem.
I am trying to set UIButton as the trigger for google sign in.
For some reason, it crushes.
I am of course adding the code I have written.
Thanks in advance!
I have tried everything around this issue written by StackOverflow.
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
return true
}
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:.
}
#available(iOS 9.0, *)
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
if(GIDSignIn.sharedInstance().handle(url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation]))
{
return true;
}else if(url.absoluteString.contains("www.mydomain.com")){
print("incoming url \(url.absoluteString)")
return true
}
return false
}
// [START openurl]
#available(iOS 8.0, *)
func application(_ application: UIApplication,
open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
if (GIDSignIn.sharedInstance().handle(url,
sourceApplication: sourceApplication,
annotation: annotation))
{
return true;
}else if(url.absoluteString.contains("www.mydomain.com")){
print("incoming url \(url.absoluteString)")
return true
}
return false;
}
}
//
// LogInViewController.swift
// Flash Chat
//
// This is the view controller where users login
// Control–Command–Space bar to get emojes
import UIKit
import Firebase
import SVProgressHUD
import GoogleSignIn
class LogInViewController: UIViewController, GIDSignInUIDelegate, GIDSignInDelegate {
//Textfields pre-linked with IBOutlets
#IBOutlet var emailTextfield: UITextField!
#IBOutlet var passwordTextfield: UITextField!
#IBOutlet weak var loginButton: UIButton!
#IBOutlet weak var upperView: UIView!
#IBOutlet weak var warningLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().delegate = self
roundCorners(button: loginButton)
let tapGesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing))
upperView.addGestureRecognizer(tapGesture)
}
func roundCorners(button: UIButton) {
button.layer.cornerRadius = 20
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.white.cgColor
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func signInViaGooglePressed(_ sender: UIButton) {
GIDSignIn.sharedInstance()?.signIn()
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
//if any error stop and print the error
if error != nil{
print(error ?? "google error")
return
}
print("Succses")
//if success display the email on label
}
#IBAction func logInPressed(_ sender: AnyObject) {
SVProgressHUD.show()
if checkDetailsValidity(){
Auth.auth().signIn(withEmail: emailTextfield.text!, password: passwordTextfield.text!) { (user, error) in
if error != nil {
self.warningLabel.text = "Incorrect email or password"
SVProgressHUD.dismiss()
}
else {
SVProgressHUD.dismiss()
print("Successfuly logged in!")
self.performSegue(withIdentifier: "goToChat", sender: self)
}
}
}
}
func checkDetailsValidity() -> Bool {
if emailTextfield.text!.isEmpty {
warningLabel.text = "Email is mandatory field"
return false
}
else if !emailTextfield.text!.contains("#") || !emailTextfield.text!.contains("."){
warningLabel.text = "Email is illegal"
return false
}
return true
}
}
you must set the key of app to the delegate of GIDSingIn in the view didLoad do this
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().clientID = "yourappkey.apps.googleusercontent.com"
GIDSignIn.sharedInstance().serverClientID = "yourappkey.apps.googleusercontent.com"
roundCorners(button: loginButton)
let tapGesture = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing))
upperView.addGestureRecognizer(tapGesture)
}
replacin youappkey for your real one. then use this function to present the login view.
func sign(_ signIn: GIDSignIn!,present viewController: UIViewController!) {
self.present(viewController, animated: true, completion: nil)
}
and this one to close the login window.
func sign(_ signIn: GIDSignIn!,dismiss viewController: UIViewController!) {
self.dismiss(animated: true, completion: nil)
}
Follow the steps to integrate GoogleSignIn https://developers.google.com/identity/sign-in/ios/sign-in?ver=swift
Also, https://www.youtube.com/watch?v=QmnI5c85sf0 visually helps setting up the sign in.
You might be missing the URL Schema to handle the sign in.
I am trying to implement facebook login in swift. I am currently getting error
The operation couldn’t be completed. (com.facebook.sdk.core error 3.)
My current implementation are as follows:
My pod file:
target 'AlamofireTest' do
# Comment the next line if you're not using Swift and don't want to use
dynamic frameworks
use_frameworks!
# Pods for AlamofireTest
pod 'Alamofire'
pod 'AlamofireImage'
pod 'FBSDKLoginKit'
target 'AlamofireTestTests' do
inherit! :search_paths
# Pods for testing
end
target 'AlamofireTestUITests' do
inherit! :search_paths
# Pods for testing
end
end
My Appdelegate file:
import UIKit
import FBSDKCoreKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FBSDKApplicationDelegate.sharedInstance()?.application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let handled = FBSDKApplicationDelegate.sharedInstance()?.application(app, open: url, options: options)
return handled!
}
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:.
}
}
and my ViewController implementation:
#IBAction func loginWithFacebook(_ sender: Any) {
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
fbLoginManager.logIn(withReadPermissions: ["public_profile","email"], from: self) { (result, error) -> Void in
if error != nil {
print(error!.localizedDescription)
self.dismiss(animated: true, completion: nil)
} else if result!.isCancelled {
print("Cancelled")
self.dismiss(animated: true, completion: nil)
} else {
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, relationship_status"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
let fbDetails = result as! NSDictionary
print(fbDetails)
}
})
}
}
}
I have followed the steps mentioned in the guide facebook login ios, and also used the suggestions in this. I am unable to find the steps, I did wrong. Any help is much appreciated.
Imprt SDKs:
FBSDKCoreKit.framework
Bolts.framework
FBSDKLoginKit.framework
In Appdelegate.swift
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool
{
return FBSDKApplicationDelegate.sharedInstance().application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
}
In ViewController.swift
#IBAction func ButtonClickFB(_ sender: Any){
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.logOut()
fbLoginManager.logIn(withReadPermissions:["email","user_friends","user_birthday"], from: self, handler: { (result, error) -> Void in
if ((error) != nil)
{
// Process error
// print(error)
}
else if (result?.isCancelled)!
{
// Handle cancellations
// print(error)
}
else
{
let fbloginresult : FBSDKLoginManagerLoginResult = result!
if(fbloginresult.grantedPermissions.contains("email"))
{
self.getFBUserData()
// fbLoginManager.logOut()
}
}
})
}
func getFBUserData () {
if((FBSDKAccessToken.current()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(normal), email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
print((result! as AnyObject))
// print(((result! as AnyObject).value(forKey: "id") as? String)!)
self.strEmail = ((result! as AnyObject).value(forKey: "email") as? String) ?? ""
self.strID = ((result! as AnyObject).value(forKey: "id") as? String) ?? ""
self.strName = ((result! as AnyObject).value(forKey: "name") as? String) ?? ""
self.TextFBEmail.text = self.strEmail
}
})
}
}
Add this in info
Add FB id in info.plist
What I want to do is segue to the appropriate view controller upon successful authentication with Firebase. So that when the user clicks "Sign In" it checks to see if the credentials are correct and then if they are it will segue to my next view controller. This is what I have and I keep getting error "Thread 1: signal SIGABRT"
Here is my viewController.swift
import UIKit
import Firebase
import GoogleSignIn
class ViewController: UIViewController, GIDSignInUIDelegate, GIDSignInDelegate {
#IBOutlet weak var txtPassword: UITextField!
#IBOutlet weak var txtEmail: UITextField!
#IBOutlet weak var txtAuthStatus: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().uiDelegate = self
if let user = Auth.auth().currentUser {
self.txtAuthStatus.text = "Signed in as " + user.email!;
}
else {
self.txtAuthStatus.text = "";
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
try! Auth.auth().signOut()
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
let authentication = user.authentication
let credential = GoogleAuthProvider.credential(withIDToken: (authentication?.idToken)!, accessToken: (authentication?.accessToken)!)
Auth.auth().signIn(with: credential, completion: { (user, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
}
print("User logged in with Google")
})
}
#IBAction func btnCreateUser(_ sender: Any) {
if let email:String = txtEmail.text, let pass:String = txtPassword.text {
Auth.auth().createUser(withEmail: email, password: pass) {
(user, error) in
if let error = error {
let errCode = AuthErrorCode(rawValue: error._code);
if (errCode == .emailAlreadyInUse) {
self.txtAuthStatus.text = "Error: user already exists";
} else {
self.txtAuthStatus.text = error.localizedDescription;
}
}
if let user = user {
self.txtAuthStatus.text = "Signed in as " + user.email!
self.txtEmail.text = nil;
self.txtPassword.text = nil;
}
}
}
}
#IBAction func btnSignIn(_ sender: Any) {
if let email:String = txtEmail.text, let pass:String = txtPassword.text {
Auth.auth().signIn(withEmail: email, password: pass) {
(user, error) in
if let error = error {
let errCode = AuthErrorCode(rawValue: error._code);
if (errCode == .userNotFound) {
self.txtAuthStatus.text = "Error: user not found";
}
else if (errCode == .wrongPassword) {
self.txtAuthStatus.text = "Error: wrong password";
}
else if (errCode == .userDisabled) {
self.txtAuthStatus.text = "Error: user account disabled";
}
else {
self.txtAuthStatus.text = error.localizedDescription;
}
}
if let user = user {
self.txtAuthStatus.text = "Signed in as " + user.email!
self.txtEmail.text = nil;
self.txtPassword.text = nil;
self.performSegue(withIdentifier: "signin", sender: self)
}
}
}
}
#IBAction func btnSignOut(_ sender: Any) {
try! Auth.auth().signOut();
self.txtAuthStatus.text = "Signed out";
}
}
and here is my AppDelegate.swift
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
return true
}
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any])
-> Bool {
return GIDSignIn.sharedInstance().handle(url,
sourceApplication:options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String,
annotation: [:])
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
return GIDSignIn.sharedInstance().handle(url,
sourceApplication: sourceApplication,
annotation: annotation)
}
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:.
}
}
I have a segue between the view controller's I want to move between and the identifier for it is "signin"
My problem was in that when I pushed to the next view controller it no longer had the text fields for email and password. By switching out the login function to a different .swift file and its view controller to a different class it no longer looked for those on loading and now it works flawlessly!
In my app, I have implemented a Google Sign In. I am trying to get it so that when the user is authorized after pressing the google sign in button, and has signed in successfully, the user will be taken to a new tab controller. So far, this works, but only when I close the app after signing in. So basically the problem is that the user is not being taken straight to the tab bar controller.
Also, if the authorization process is supposed to take this long, or is supposed to be like this, I would like it if you could tell me how to show an activity indicator, so that the user knows that the process takes a while.
Thanks in advance!
Here is a short video showing this:
Google Sign In Video
ViewController.swift code:
import UIKit
import Firebase
import GoogleSignIn
class ViewController: UIViewController, GIDSignInUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupGoogleButtons()
}
fileprivate func setupGoogleButtons() {
//add google sign in button
let googleButton = GIDSignInButton()
googleButton.frame = CGRect(x: 16, y: 116 + 66, width: view.frame.width - 32, height: 50)
view.addSubview(googleButton)
//custom google button
let customButton = UIButton(type: .system)
customButton.frame = CGRect(x: 16, y: 116 + 66 + 66, width: view.frame.width - 32, height: 50)
customButton.backgroundColor = .orange
customButton.setTitle("Custom Google Sign In", for: .normal)
customButton.addTarget(self, action: #selector(handleCustomGoogleSign), for: .touchUpInside)
customButton.setTitleColor(.white, for: .normal)
customButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
view.addSubview(customButton)
GIDSignIn.sharedInstance().uiDelegate = self
}
func handleCustomGoogleSign() {
GIDSignIn.sharedInstance().signIn()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
AppDelegate code:
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FIRApp.configure()
GIDSignIn.sharedInstance().clientID = FIRApp.defaultApp()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
if GIDSignIn.sharedInstance().hasAuthInKeychain() {
print("User has been successfully signed in with Google")
let sb = UIStoryboard(name: "Main", bundle: nil)
if let tabBarVC = sb.instantiateViewController(withIdentifier: "TabController") as? UITabBarController {
window!.rootViewController = tabBarVC
}
} else {
print("User has failed in signing in with Google")
let sb = UIStoryboard(name: "Main", bundle: nil)
if let tabBarVC = sb.instantiateViewController(withIdentifier: "LogInViewController") as? ViewController {
window!.rootViewController = tabBarVC
}
}
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google", user)
guard let idToken = user.authentication.idToken else { return }
guard let accessToken = user.authentication.accessToken else { return }
let credentials = FIRGoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
FIRAuth.auth()?.signIn(with: credentials, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
guard let uid = user?.uid else { return }
print("Successfully logged into Firebase with Google", uid)
})
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
GIDSignIn.sharedInstance().handle(url,
sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String!,
annotation: options[UIApplicationOpenURLOptionsKey.annotation])
return true
}
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:.
}
}
As I wrote in the title of this question - I went through this tutorial https://developers.google.com/identity/sign-in/ios/sign-in and now I'm able to log in the user to my app based on his google credentials.
The way I do it so far is that I have a ViewController.swift class with the following code:
class ViewController: UIViewController, GIDSignInUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let background = CAGradientLayer().greenBlue()
background.frame = self.view.bounds
self.view.layer.insertSublayer(background, atIndex: 0)
//GIDSignIn.sharedInstance().uiDelegate = self
// Uncomment to automatically sign in the user.
//GIDSignIn.sharedInstance().signInSilently()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signInSilently()
}
#IBAction func didTapSignOut(sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func signInWillDispatch(signIn: GIDSignIn!, error: NSError!) {
print("Nothing!")
}
// Present a view that prompts the user to sign in with Google
func signIn(signIn: GIDSignIn!,
presentViewController viewController: UIViewController!) {
self.presentViewController(viewController, animated: true, completion: nil)
}
// Dismiss the "Sign in with Google" view
func signIn(signIn: GIDSignIn!,
dismissViewController viewController: UIViewController!) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
and in my AppDelegate.swift class I have:
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
GIDSignIn.sharedInstance().delegate = self
return true
}
// [START openurl]
func application(application: UIApplication,
openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return GIDSignIn.sharedInstance().handleURL(url,
sourceApplication: sourceApplication,
annotation: annotation)
}
// [END openurl]
#available(iOS 9.0, *)
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
return GIDSignIn.sharedInstance().handleURL(url,
sourceApplication: options[UIApplicationOpenURLOptionsSourceApplicationKey] as! String?,
annotation: options[UIApplicationOpenURLOptionsAnnotationKey])
}
// [START signin_handler]
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!,
withError error: NSError!) {
if (error == nil) {
print("Signed in!")
} else {
print("\(error.localizedDescription)")
}
}
// [END signin_handler]
// [START disconnect_handler]
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user:GIDGoogleUser!,
withError error: NSError!) {
// Perform any operations when the user disconnects from app here.
// [START_EXCLUDE]
NSNotificationCenter.defaultCenter().postNotificationName(
"ToggleAuthUINotification",
object: nil,
userInfo: ["statusText": "User has disconnected."])
// [END_EXCLUDE]
}
}
My storyboard looks as follows:
On the left I have a ViewController with marked google button (which is white, therefore it's not visible here - sorry!) and on the right I have a main TabController view that contains the whole logic of my app (so far it's quite empty):
class TabController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let background = CAGradientLayer().greenBlue()
background.frame = self.view.bounds
self.view.layer.insertSublayer(background, atIndex: 0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Now it works like this:
I run the app and see the google button to sign in. I sign in and when everything is validated - nothing changes, I'm not moving to the second screen (TabController). I just stay on this view and I can keep clicking the google button - nothing changes though because I'm already signed in.
I want to achieve a situation when the user opens my app and when he is not logged in - he sees the ViewController screen. When he logs in - he sees the TabController screen. And also when he already signed in before and opens my app - he immediately jumps to TabController and skips the ViewController page. How can I achieve it?
I suspect I have to mark on my storyboard my TabController to be the initial view controller, but what about the log in screen?
=====EDIT
following Mac Bellingrath answer I modified my function in appDelegate.swift class, now it looks like this:
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
GIDSignIn.sharedInstance().delegate = self
/* check for user's token */
if GIDSignIn.sharedInstance().hasAuthInKeychain() {
/* Code to show your tab bar controller */
print("user is signed in")
let sb = UIStoryboard(name: "Main", bundle: nil)
if let tabBarVC = sb.instantiateViewControllerWithIdentifier("TabController") as? UITabBarController {
window!.rootViewController = tabBarVC
}
} else {
print("user is NOT signed in")
/* code to show your login VC */
let sb = UIStoryboard(name: "Main", bundle: nil)
if let tabBarVC = sb.instantiateViewControllerWithIdentifier("ViewController") as? ViewController {
window!.rootViewController = tabBarVC
}
}
return true
}
Now when I run the app and the user has been signed in before - I see the tabController view. If he wasn't signed in before - I see the ViewController view. It works almost like a charm, but I realized that no matter if I write if GIDSignIn.sharedInstance().hasAuthInKeychain() { or if !GIDSignIn.sharedInstance().hasAuthInKeychain() { it always prints out
the message user is signed in, so something is still not right...
According to Google's documentation, it appears that you can use GIDSignIn's instance method hasAuthInKeychain() to check whether the user has either currently signed in or has previous authentication saved in keychain.
So, in your AppDelegate's
func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
You might write something like:
GIDSignIn.sharedInstance().delegate = self
/* check for user's token */
if GIDSignIn.sharedInstance().hasAuthInKeychain() {
/* Code to show your tab bar controller */
} else {
/* code to show your login VC */
}
for example if our user is signed in:
let sb = UIStoryboard(name: "Main", bundle: nil)
if let tabBarVC = sb.instantiateViewControllerWithIdentifier("MainTabBarVC") as? UITabBarController {
window.rootViewController = tabBarVC
}
*Substitute your UITabBarController subclass for UITabBarController.
Of course, there are many ways to change the flow. Check out storyboard reference and multiple storyboards.
let googleUser = GIDSignIn.sharedInstance().isCurrentUser
if googleUser != nil {
// Get new token from google and send to server
let strToken = googleUser.authentication.idToken
} else {
// present login screen here
presentLoginScreen()
}
Mac Bellingrath given perfect answer for verify user is logged in.
Alternate way to check user logged in.
Here is sample code in Objective-C you can convert code as per your requirement.
This sample code is just for your reference.
// The authentication object for the current user, or |nil| if there is currently no logged in user.
// Here you can get current user object
GIDGoogleUser *googleUser = [[GIDSignIn sharedInstance] currentUser];
if (googleUser) {
// Get new token from google and send to server
NSString *strToken = googleUser.authentication.idToken;
}
else {
// present login screen here
[self presentLoginScreen];
}