28

I am developing an ios app using the Facebook SDK to login. I have set a LogInViewController as the initial View Controller in the Storyboard, from where the user logins using the FB account.

I have another ViewController which is loaded correctly once the user logs in.

In the AppDelegate file I am checking for currentAccessToken and if it is not nil, I am loading directly the second ViewController, because the user is already logged in.

However, the currentAccessToken is always nil if I quit the app and relaunch it. It only works if I press the home button and re-open the app while it's still running in the background.

Here are the details in the code:

AppDelegate.swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    self.customNavigationBar()
    if (!isIcloudAvailable()) {
        self.displayAlertWithTitle("iCloud", message: "iCloud is not available." +
           " Please sign into your iCloud account and restart this app")
        return true
    }

    if (FBSDKAccessToken.currentAccessToken() != nil) {
        self.instantiateViewController("MapViewController", storyboardIdentifier: "Main")
    }

    return FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
}

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) {
        FBSDKAppEvents.activateApp()
}

func applicationDidBecomeActive(application: UIApplication) {
        FBSDKAppEvents.activateApp()
}

LogInViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()    
    // Listen to the Facebook notification and when received, execute func handleFBSessionStateChangeWithNotification
NSNotificationCenter.defaultCenter().addObserver(self, selector:"handleFBSessionStateChangeWithNotification:", name: "SessionStateChangeNotification", object: nil)
}

func handleFBSessionStateChangeWithNotification(notification: NSNotification) {
    // Switch to MapViewController when logged in
    if ((FBSDKAccessToken.currentAccessToken()) != nil) {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let mapViewController = storyboard.instantiateViewControllerWithIdentifier("MapViewController") as! MapViewController
        self.presentViewController(mapViewController, animated: false, completion: nil)
    }
}

I don't know if it is related, but I am also getting a warning for the MapViewController because there is no segue put towards it from the Storyboard:

Warning: Attempt to present MapViewController whose view is not in the window hierarchy!

Tom Elliott
  • 1,908
  • 1
  • 19
  • 39
Andreas Xenos
  • 335
  • 1
  • 3
  • 7
  • FBAccessToken.currentAccessToken takes some time to be populated. You should wait for it to be available using the sessionStateChangedNotification (as you have already done). – ZeMoon Oct 05 '15 at 14:47
  • Thank you for your answer. How much time does it need? That means that if user quits the app quite fast, he will need to re-login? I remember in previous FBSDK versions, there was a check for cached access token, which now I don't know how to do that. – Andreas Xenos Oct 05 '15 at 15:21
  • I have tried to put the sessionsStateChangedNotification in viewDidAppear of the LoginViewController but nothing changed. – Andreas Xenos Oct 06 '15 at 07:12

4 Answers4

57

The problem is because you are calling for FBSDKAccessToken.currentAccessToken() before having called

FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)

You can check for the access token anytime after calling the above line.

EDIT: Explanation

The above line lets the Facebook SDK process the launchOptions and extract the necessary information which it will require to recognise and persist the user for the application.

In cases where the user is already logged in, this simply initialises the Facebook SDK which in turn logs in the user on the basis of persisted data.

ZeMoon
  • 20,054
  • 5
  • 57
  • 98
  • @ZeMoon can you please elaborate your answer. – Kaunteya Apr 05 '16 at 18:13
  • @Kaunteya The Facebook SDK needs a chance to be initialised before you do any operation on it. Could you be more specific in what to elaborate? – ZeMoon Apr 06 '16 at 04:34
  • 6
    This doesnt work for me. FBSDKAccessToken.currentAccessToken() gives a correct token when login. But will be nil after killing the app. The appdelefate method you mention doesnt make any difference. – omarojo Sep 09 '16 at 03:33
  • Doesn't work for me either. The above line is in the AppDelegate and gets called before FBSDKAccessToken.current() method. Still I get a nil token – victor Jan 29 '17 at 11:27
  • @ZeMoon. What launchOptions to pass to this method? – zulkarnain shah Dec 28 '17 at 03:57
  • @zulkarnainshah Pass the parameters passed to the `didFinishLaunchingWithOptions` App Delegate method as is. – ZeMoon Dec 28 '17 at 05:11
  • @ZeMoon Can I get currentAccessToken in an updated version of my App. assuming that user has logged in to his FB account in the old version of my App. As I am getting always nil even keychain sharing is ON in old version as well as in the new version of my App. I want to provide him a seamless experience if the user is just updating App. unfortunately I did not explicitly saved it in the old version and that is already published – Manish Nahar Jan 11 '18 at 08:12
  • 1
    It did work for me. If it's not working for you, please make sure that you're calling this from inside your AppDelegate, when overriding `-[application:didFinishLaunchingWithOptions:]`, and you pass to `FBSDKApplicationDelegate` the same parameters you receive there (`application` and `launchOptions`) – Gobe Oct 21 '18 at 16:12
15

I spent a nice half-day banging my head against this issue. In spite of making sure all the delegate methods were present, the FBSDKAccessToken.current() always returned nil.

It turns out that this is because Keychain Sharing is not enabled ( Xcode 8, iOS 10). To fix, go to App -> Capabilities -> Keychain Sharing and turn ON.

Once this is done, you have to still go through the authorization process and return back to the app. Everything should be fine after.

Kal
  • 24,724
  • 7
  • 65
  • 65
  • Tried this too, still not working for me. Simulator just looses its session. Could it be related to this string I get in the logs: 'Falling back to storing access token in NSUserDefaults because of simulator bug' – victor Jan 29 '17 at 11:31
  • @victor - did you verify the problem continues testing a on a real device? – syllabix Jan 29 '17 at 18:25
  • Didn't try it yet on a real device. Seems that it should be working on the simulator - curious if anyone had it working on the device and not the simulator – victor Jan 29 '17 at 18:29
0

If you already tried the upon solutions, but still have the problem, try this.

My co-programmer and I are using the LoginManager().login method provided in Facebook Swift SDK. (Xcode8, Swift 3, iOS 10)

One possible action that caused that issue is when you login successfully and kill the app immediately, then open your app again and the AccessToken.current returns nil. After login, if you wait 20 seconds or longer (we are not sure the exact waiting time, we waited for 7-20 seconds) and kill the app, the problem seemed to be solved.

We are not sure why this is happened, but this solved our problem, and our guess is that there may be a network delay or a bug.

Vemonus
  • 868
  • 1
  • 16
  • 28
MnS
  • 101
  • 4
0

For those who still having the problem even after set the FBSDKApplicationDelegate.sharedInstance() on the appdelegate, turn out if u get error on your Xcode console like this one

Falling back to storing access token in NSUserDefaults because of simulator bug

it is simulator bug and i try using real device and it work, the access token is not nil again.. so maybe answer @victor comment about this one on @Kal answer..

Abadi
  • 112
  • 10