I wrote a basic Swift Class for Facebook login handling.
I want to check if the user already authorised the App, because in my case, the user gets asked everytime if he authorises the app - it switches to safari everytime instead of simply logging in. Sometimes the login completely fails - no error message, but also no success.
Here is my code:
class FacebookLogin{
private var data : NSMutableData? = nil
init(){
}
let facebookReadPermissions = ["public_profile", "email", "user_friends"]
func loginToFacebookWithSuccess(successBlock: () -> (), andFailure failureBlock: (NSError?) -> ()) {
if FBSDKAccessToken.currentAccessToken() != nil {
return
}
FBSDKLoginManager().logInWithReadPermissions(self.facebookReadPermissions, handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if error != nil {
// Process error
FBSDKLoginManager().logOut()
failureBlock(error)
} else if result.isCancelled {
// Handle cancellations
FBSDKLoginManager().logOut()
failureBlock(nil)
} else {
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
var allPermsGranted = true
let grantedPermissions = Array(result.grantedPermissions).map( {"\($0)"} )
for permission in self.facebookReadPermissions {
if !contains(grantedPermissions, permission) {
allPermsGranted = false
break
}
}
if allPermsGranted {
// Do work
let fbToken = result.token.tokenString
let fbUserID = result.token.userID
println(fbUserID)
successBlock()
} else {
//The user did not grant all permissions requested
failureBlock(nil)
}
}
})
}
func login() -> Void{
if(self.alreadyLoggedIn() == false){
self.performLogin()
}else{
self.getFBUserData()
}
}
private func performLogin() -> Void {
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager();
fbLoginManager.loginBehavior = FBSDKLoginBehavior.SystemAccount
println(FBSDKAccessToken.currentAccessToken())
fbLoginManager.logInWithReadPermissions(["email"], handler: { (result, error) -> Void in
if (error == nil){
var fbloginresult : FBSDKLoginManagerLoginResult = result
if(fbloginresult.grantedPermissions.contains("email"))
{
self.getFBUserData()
fbLoginManager.logOut()
}
}
})
}
private func alreadyLoggedIn() -> Bool {
if (FBSDKAccessToken.currentAccessToken() != nil)
{
return true
}else{
return false
}
}
private func getFBUserData() -> Void{
if((FBSDKAccessToken.currentAccessToken()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).startWithCompletionHandler({ (connection, result, error) -> Void in
if (error == nil){
println(result)
}
})
}
}
}
Thanks
In your view controller viewDidLoad() you can add this code :
if (FBSDKAccessToken.currentAccessToken() == nil)
{
// User is not already logged
println("No Logged")
}
else
{
// User is already logged
println("Already Logged")
}
Related
I'm writing an iOS app that has to use google sign in. It compiles fine but when it runs and I try to sign in with a button, the sign in flow is interrupted:
This is the error: Optional("The operation couldn’t be completed. (com.google.GIDSignIn error -4.)")
error with signing in: Optional(Error Domain=com.google.GIDSignIn Code=-5 "The user canceled the sign-in flow." UserInfo={NSLocalizedDescription=The user canceled the sign-in flow.})
In the simulator, when I press the button, it will show the dialog box asking whether it is ok for the app to access google and then it will just disappear after a second or two.
Here is my ViewController:
//
// ViewController.swift
// frcscout
//
// Created by Elliot Scher on 12/20/22..
//
import UIKit
import GoogleSignIn
import GTMSessionFetcher
import GoogleAPIClientForREST
class ViewController: UIViewController {
#IBOutlet weak var signInButton: UIButton!
private let service = GTLRSheetsService()
override func viewDidLoad() {
super.viewDidLoad()
googleSignIn { signInStatus in
if signInStatus == true {
self.signInButton?.setTitle("Sign out", for: .normal)
print("Signed in!")
} else {
self.signInButton?.setTitle("Sign in", for: .normal)
print("Issues with signing in...")
}
}
}
#IBAction func signInPressed(_ sender: UIButton) {
print("Sign in pressed")
let user = GIDSignIn.sharedInstance.currentUser
if (user == nil) {
googleSignIn() { success in
if success == true {
sender.setTitle("Sign out", for: UIControl.State.normal)
} else {
return
}
}
} else {
GIDSignIn.sharedInstance.signOut()
self.service.authorizer = nil
sender.setTitle("Sign in", for: UIControl.State.normal)
}
}
}
extension ViewController {
func googleSignIn(completionHandler: #escaping (Bool) -> Void) {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error == nil {
print("Managed to restore previous sign in!\nScopes: \(String(describing: user?.grantedScopes))")
self.requestScopes(googleUser: user!) { success in
if success == true {
completionHandler(true)
} else {
completionHandler(false)
}
}
} else {
print("No previous user!\nThis is the error: \(String(describing: error?.localizedDescription))")
let signInConfig = GIDConfiguration.init(clientID: K.clientID)
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { gUser, signInError in
if signInError == nil {
self.requestScopes(googleUser: gUser!) { signInSuccess in
if signInSuccess == true {
completionHandler(true)
} else {
completionHandler(false)
}
}
} else {
print("error with signing in: \(String(describing: signInError)) ")
self.service.authorizer = nil
completionHandler(false)
}
}
}
}
}
func requestScopes(googleUser: GIDGoogleUser, completionHandler: #escaping (Bool) -> Void) {
let grantedScopes = googleUser.grantedScopes
if grantedScopes == nil || !grantedScopes!.contains(K.grantedScopes) {
let additionalScopes = K.additionalScopes
GIDSignIn.sharedInstance.addScopes(additionalScopes, presenting: self) { user, scopeError in
if scopeError == nil {
user?.authentication.do { authentication, err in
if err == nil {
guard let authentication = authentication else { return }
// Get the access token to attach it to a REST or gRPC request.
// let accessToken = authentication.accessToken
let authorizer = authentication.fetcherAuthorizer()
self.service.authorizer = authorizer
completionHandler(true)
} else {
print("Error with auth: \(String(describing: err?.localizedDescription))")
completionHandler(false)
}
}
} else {
completionHandler(false)
print("Error with adding scopes: \(String(describing: scopeError?.localizedDescription))")
}
}
} else {
print("Already contains the scopes!")
completionHandler(true)
}
}
}
Can anyone help me fix this? Thanks!!
I'm using Firebase database and offer anonymous login. The first anonymous login made on a single device works as expected. If I sign out and attempt any more anonymous logins, it succeeds, the completion block has no error and returns a user.
However, once it's all done and we're out of the completion block, Auth.auth().currentUser() is nil.
If I run a simple Timer checking Auth.auth().currentUser() every second, throughout the entire login process it is always nil and never changes.
Quick breakdown of code:
Login anonymously.
Check if id exists in db.
Update profile displayName with id for easy referral later.
Fetch client in db.
All go wrong!
Tap a button to sign in.
#IBAction func clientLoginBtnTap(_ sender : UIButton) {
self.clientActivityIndicator?.showActivityIndicator()
Auth.auth().signInAnonymously { (user, error) in
if error == nil {
//check id matches available client
self.checkClient(id: (self.clientIdField?.text)!, completion: { (isValid) in
if isValid == true {
//now signed in, update client id
let profileChangeRequest = user?.createProfileChangeRequest()
profileChangeRequest?.displayName = self.clientIdField?.text
profileChangeRequest?.commitChanges(completion: { (error) in
if error == nil {
//done
UserDefaults.standard.set(true, forKey: kIS_USER_CLIENT_NOT_TRAINER)
self.dismiss(animated: true, completion: {
//self.delegate?.didLoginAsClient()
})
}
else {
self.logout()
self.clientIdField?.shake()
self.clientActivityIndicator?.hideActivityIndicator()
}
})
}
else {
self.logout()
self.clientIdField?.shake()
self.clientActivityIndicator?.hideActivityIndicator()
}
})
}
else {
}
}
}
func checkClient(id : String, completion: #escaping (_ isValid : Bool) -> Void) {
let ref = Database.database().reference().child("v2").child("clients").child(id)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() { completion(true) }
else { completion(false) }
}) { (error) in
completion(false)
}
}
func logout() {
do {
try Auth.auth().signOut()
}
catch let error as NSError {
print (error.localizedDescription)
}
}
Login is successful.
Then this runs after login and the user exists but Auth.auth().currentUser() is nil. When a client login happens, I try to get the client data but permission is denied because we have no user.
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
self.currentUser = user
if user == nil {
self.updateForNoUser()
}
else {
self.updateForUser()
}
}
func updateForUser() {
//Trainer Logged in
if UserDefaults.standard.bool(forKey: kIS_USER_CLIENT_NOT_TRAINER) == false {
self.performSegue(withIdentifier: "master", sender: self)
}
//Client Logged in
else {
if let id = Auth.auth().currentUser?.displayName {
let ref = Database.database().reference().child("v2").child("clients").child(id)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
self.client = Client(snapshot: snapshot)
self.performSegue(withIdentifier: "masterClient", sender: self)
}
}) { (error) in }
}
else {
do {
try Auth.auth().signOut()
}
catch let error as NSError {
print (error.localizedDescription)
}
}
}
}
I'm trying to pass a property to my next VC using prepareforSegue. It is called, however my other VC does not load.
Original
#IBAction func onSignupPressed(sender: AnyObject) {
FBSDKLoginManager().logInWithReadPermissions(permissions,
fromViewController: self) { result, error in
guard error == nil else { print("Login Error"); return }
guard result.grantedPermissions.contains("email") else {
print("No Email Permissions"); return }
self.getFBUserData()
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginSegue", sender: self)
}
}
}
I've been able to get the next VC showing by doing calling my doLogin function below. And there I instantiate a VC from the storyboard and present it. I believe it is due to the timing of the Facebook login window that pops up and closes. I searched for a delegate method, but have not found anything
#IBAction func onSignupPressed(sender: AnyObject) {
FBSDKLoginManager().logInWithReadPermissions(permissions,
fromViewController: self) { result, error in
guard error == nil else { print("Login Error"); return }
guard result.grantedPermissions.contains("email") else {
print("No Email Permissions"); return }
self.getFBUserData()
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginSegue", sender: self)
}
}
}
func getFBUserData(){
let params = "id, name, first_name, last_name, picture.type(large),friends, email, birthday, work, photos, education, location, hometown, religion, likes, about"
if((FBSDKAccessToken.currentAccessToken()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields":params]).startWithCompletionHandler({ (connection, result, error) -> Void in
if (error == nil){
//everything works print the user data
print(result)
let resultdict = result as? NSDictionary
self.user = Mapper<User>().map(result)
self.doLogin()
}
})
}
}
func doLogin() {
let successVC = self.storyboard?.instantiateViewControllerWithIdentifier("LoginSucessViewController")
self.presentViewController(successVC!, animated: true, completion: nil)
}
Very silly mistake! Didn't realize I could simply set the property of instantiating the VC!
func doLogin() {
let successVC = self.storyboard?.instantiateViewControllerWithIdentifier("LoginSucessViewController") as! LoginSucessViewController
successVC.currentUser = user
self.presentViewController(successVC, animated: true, completion: nil)
}
I added FBSDK 4.7 to my swift app and when it opens the safari view controller to login, and I try to tap "Done" without inputting anything into the email/password fields, the app breaks. It spits out "fatal error: unexpectedly found nil while unwrapping an Optional value"
How do I handle this error?
Current login code:
if let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager(){
fbLoginManager.logInWithReadPermissions(["email"],fromViewController:self,
handler:{(result:FBSDKLoginManagerLoginResult!,error:NSError!) -> Void in
if (error == nil){
let fbloginresult : FBSDKLoginManagerLoginResult = result
if(fbloginresult.grantedPermissions.contains("email"))
{
print("Logged in successfully")
self.getFBUserData()
//fbLoginManager.logOut()
}
}
else{
print("HELPPPPPPPPPP")
}
})
}
No need to assign the types of variable (result, error) :
Here is working code with Facebook 4.7 SDK | Swift 2 | Xcode 7.
#IBAction func btnFBLoginPressed(sender: AnyObject) {
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.loginBehavior = FBSDKLoginBehavior.Web
fbLoginManager.logInWithReadPermissions(["email"], fromViewController: self, handler: { (result, error) -> Void in
if (error == nil){
let fbloginresult : FBSDKLoginManagerLoginResult = result
if fbloginresult.grantedPermissions != nil { //Here we have to check that the set contains value or not
if(fbloginresult.grantedPermissions.contains("email"))
{
self.getFBUserData()
fbLoginManager.logOut()
}
}
}
})
}
func getFBUserData(){
if((FBSDKAccessToken.currentAccessToken()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).startWithCompletionHandler({ (connection, result, error) -> Void in
if (error == nil){
print(result)
}
})
}
}
We have to check the grantedPermissions's Set is nil or not.
if fbloginresult.grantedPermissions != nil { //Here we have to check that the set contains value or not
The problem here is that the player is not getting authenticated and I'm not getting the log-in window for game center so I really need to get this fixed as soon as possible.
func authenticateLocalPlayer() {
var localPLayer = GKLocalPlayer.localPlayer()
localPLayer.authenticateHandler = {(viewController : UIViewController!, error : NSError!) -> Void in
if viewController != nil {
self.presentViewController(viewController, animated: true, completion: nil)
} else {
if localPLayer.authenticated {
self.gameCenterEnabled = true
localPLayer.loadDefaultLeaderboardIdentifierWithCompletionHandler({ (leaderboardIdentifier : String!, error : NSError!) -> Void in
if error != nil {
println(error.localizedDescription)
} else {
self.leaderboardIdentifier = leaderboardIdentifier
}
})
} else {
self.gameCenterEnabled = false
}
}
}
}