Search code examples
iosswiftfacebook-loginfacebook-access-tokenfbsdk

Facebook AccessToken nil after killing App (iOS)


I am developing an iOS app using the FacebookSDK to login.

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

I started using alert since logs are not working properly after killing the App.

AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

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

    let accessToken = FBSDKAccessToken.current()
    if accessToken == nil {
        Auth.auth().addStateDidChangeListener { (auth, user) in
            alert(title: user?.uid ?? "1")
        }
        alert(title: "2")
    } else {
        alert(title: accessToken?.userID ?? "3")
    }
}

It always displays "1" in the Alert.

It is a similar problem to: FBSDKAccessToken currentAccessToken nil after quitting app or iOS 9 Facebook Access Token is nil on future app launches

I have been tried:

  1. Keychain Not working
  2. Downgrading to 4.17 not possible via Cocoapods
  3. Changing Transport Security not working as well

Help/Suggestions,

Thanks in advance.


Solution

  • AccessToken.current does not persist and hence you would have to manually save the access token string and recreate it on consecutive launches.

    Basically:

    1. Save when you get Access token

      let strAuthenticationToken = accessToken.authenticationToken
      UserDefaults.standard.set(strAuthenticationToken,
                            forKey: "AccessToken_Facebook")
      
    2. Get Access Token string and recreate the AccessTokenobject on next launch

      if let strAuthenticationToken = UserDefaults.standard.string(forKey: "AccessToken_Facebook") {
      //Create AccessToken for facebook login
      let accessToken = AccessToken(authenticationToken: strAuthenticationToken)
      
      //...Skip Login and directly proceed to get facebook profile data with the AccessToken
      }
      

    NOTE: For the ease of this example, the Facebook Access Token string is being saved to UserDefaults but ideally it should be saved to the Keychain.


    In Example:

    func loginWithFacebook() {
        //Check for previous Access Token
        if let accessToken  = AccessToken.current {
            //AccessToken was obtained during same session
            getAccountDetails(withAccessToken: accessToken)
        }
        else if let strAuthenticationToken = UserDefaults.standard.string(forKey: "AccessToken_Facebook") {
            //A previous access token string was saved so create the required AccessToken object
            let accessToken = AccessToken(authenticationToken: strAuthenticationToken)
    
            //Skip Login and directly proceed to get facebook profile data with an AccessToken
            getAccountDetails(withAccessToken: accessToken)
        }
        else {
            //Access Token was not available so do the normal login flow to obtain the Access Token
            doFacebookLogin()
        }
    }
    

    func doFacebookLogin() {
        LoginManager().logIn(readPermissions: [.publicProfile, .email], viewController: nil) { loginResult in
            switch loginResult {
            case .failed(let error):
                print(error)
    
            case .cancelled:
                print("User cancelled login.")
    
            case .success(let grantedPermissions,
                          let declinedPermissions,
                          let accessToken):
    
                //Save Access Token string for silent login purpose later
                let strAuthenticationToken = accessToken.authenticationToken
                UserDefaults.standard.set(strAuthenticationToken,
                                          forKey: "AccessToken_Facebook")
    
                //Proceed to get facebook profile data
                self.getAccountDetails(withAccessToken: accessToken)
            }
        }
    }
    

    func getAccountDetails(withAccessToken accessToken: AccessToken) {
        let graphRequest: GraphRequest = GraphRequest(graphPath     : "me",
                                                      parameters    : ["fields" : "id, name, email"],
                                                      accessToken   : accessToken,
                                                      httpMethod    : GraphRequestHTTPMethod.GET,
                                                      apiVersion    : GraphAPIVersion.defaultVersion)
        graphRequest.start { (response, result) in
            switch result {
            case .success(let resultResponse):
                print(resultResponse)
            case .failed(let error):
                print(error)
            }
        }
    }
    

    (Swift 4 / Facebook SDK 4.30)