Facebook SDK 3.1 iOS: Handle login if user remove app from Facebook Settings
I found this happening to myself as well.... It was discovered when a user changed their facebook password, and we could no longer authenticate. Doing a manual resync / clear of the account session allowed them to login again.
-(void)syncFacebookAccount { [self forceLogout]; ACAccountStore *accountStore = [[ACAccountStore alloc] init]; ACAccountType *accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:@"com.apple.facebook"]; if (accountStore && accountTypeFB) { NSArray *fbAccounts = [accountStore accountsWithAccountType:accountTypeFB]; id account; if (fbAccounts && [fbAccounts count] > 0 && (account = [fbAccounts objectAtIndex:0])) { [accountStore renewCredentialsForAccount:account completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) { // Not actually using the completion handler... }]; } } }
It's actually VERY hard to parse out and detect these types of errors for two reasons:
- I cannot figure out any way to detect this problem until you actually attempt and fail an FBRequest (or similar), and
- The
NSError
object passed from the failedFBRequest
is ABSURDLY difficult to parse and use in any functional way.
Below is the crazy method I wrote while literally pulling an all-nighter to deal with this. It handles the NSError *error
object from the failed FBRequest
attempt, and passes it to the relevant methods OR displays the most sensible error I could find (or hits the bottom catch-all).
Note the comments - particularly around innerError
and parsedResponse
- that detail what I've discovered so far. Good luck, brave soldier:
- (void)handleFacebookError:(NSError *)error withPermissionType:(RMFacebookPermissionsType)type // this is just a typedef enum specifying Write or Read permissions so I can react accordingly withCompletion:(void (^)(BOOL retry))completionBlock { newMethodDebugLog; NSParameterAssert(error); NSParameterAssert(type); NSParameterAssert(completionBlock); // the completion block tells the controller whether the error is 'fatal' or can be recovered - if YES, it can be recovered // this is the parsed result of the graph call; some errors can appear here, too, sadly NSDictionary *parsedResponse = [error.userInfo objectForKey:@"com.facebook.sdk:ParsedJSONResponseKey"]; int parsedErrorCode = [[[[parsedResponse objectForKey:@"body"] objectForKey:@"error"] objectForKey:@"code"] intValue]; // this is an instance of NSError created by Facebook; it contains details about the error NSError *innerError = [error.userInfo objectForKey:@"com.facebook.sdk:ErrorInnerErrorKey"]; // innerError is usually un-recoverable if (innerError) { // innerError seems to be the response given in true HTTP problems; DebugLog(@"______innerError FOUND______"); DebugLog(@"innerError: %@",innerError); DebugLog(@"innerError.code: %d",innerError.code); // digging deep enough, you can actually find a coherent error message! :D DebugLog(@"innerError.localizedDescription: %@",innerError.localizedDescription); if (![alert isVisible]) { NSString *errorString = @"Facebook Connection Failed"; NSString *okString = @"OK"; alert = [[UIAlertView alloc] initWithTitle:errorString message:innerError.localizedDescription delegate:nil cancelButtonTitle:okString otherButtonTitles:nil]; [alert show]; } else { DebugLog(@"Alert already showing!"); } completionBlock(NO); } else if (parsedResponse && parsedErrorCode != 2) { // I honestly forget what error 2 is.. documentation fail :( // parsedResponses can usually be recovered DebugLog(@"parsed response values: %@",[parsedResponse allValues]); switch (parsedErrorCode) { case 2500: case 200: case 190: { DebugLog(@"parsedError code hit! forcing re-login."); // all errors in case 190 seem to be OAuth issues // http://fbdevwiki.com/wiki/Error_codes#Parameter_Errors // if needed, "error_subcode" 458 == user has de-authorized your app // case 2500 reported while grabbing from a photo album & not logged in // case 200 "requires extended permission: publish_actions" if (type == RMFacebookPermissionsTypeRead) { [self _getFacebookReadPermissionsWithUI:YES completion:completionBlock]; } else if (type == RMFacebookPermissionsTypeWrite) { [self _getFacebookWritePermissionsWithUI:YES completion:completionBlock]; } break; } default: completionBlock(YES); break; } } else { if (![alert isVisible]) { NSString *errorString = @"Facebook Error"; NSString *messageString = @"Mixture Photos was unable to connect to Facebook on your behalf. This is usually a temporary problem. Please try again later."; NSString *okString = @"OK"; alert = [[UIAlertView alloc] initWithTitle:errorString message:messageString delegate:nil cancelButtonTitle:okString otherButtonTitles:nil]; [alert show]; } else { DebugLog(@"Alert already showing!"); } completionBlock(NO); }}
I have the same problem and could not find proper documentation about error codes on Facebook SDK site.
I solved problem by comparing the [error code];
or [error userInfo];
value.
Your session will have the state FBSessionStateClosedLoginFailed
and userInfo dictionary of error will have the following form
"com.facebook.sdk:ErrorLoginFailedReason" = "com.facebook.sdk:ErrorLoginFailedReason";
On the other hand error code shows me 2
so that you can handle it at the end of sessionStateChanged::: function
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState)state error:(NSError *)error {switch (state) { case FBSessionStateOpen: { //update permissionsArrat [self retrieveUSerPermissions]; if (!needstoReopenOldSession) { //First User information [self getUserInformation:nil]; } NSNotification *authorizationNotification = [NSNotification notificationWithName:facebookAuthorizationNotification object:nil]; [[NSNotificationCenter defaultCenter] postNotification:authorizationNotification]; } case FBSessionStateClosed: { break; } case FBSessionStateClosedLoginFailed: { [FBSession.activeSession closeAndClearTokenInformation]; break; } default: break;}if (error) { NSNotification *authorizationNotification = [NSNotification notificationWithName:faceBookErrorOccuredNotification object:error]; [[NSNotificationCenter defaultCenter] postNotification:authorizationNotification];}}