Facebook SDK 3.1 iOS: обрабатывать логин, если пользователь удаляет приложение из настроек Facebook

Я хочу добавить некоторую интеграцию Facebook в свое приложение. На этом этапе мне удалось войти в систему, опубликовать на стене друзей, получить список друзей и т.д. Все в порядке, за исключением одной вещи...

Если пользователь удаляет приложение из ваших настроек/приложений Facebook а затем входит в приложение iOS, код не распознает, что приложение Facebook было удалено из пользовательских настроек и предполагает, что он зашел в систему (это проблема, потому что если пользователь пытается отправить сообщение на стену друга, приложение ничего).

Затем пользователь закрывает приложение iOS и перезапускает его... С этой перезагрузкой приложение iOS фиксируется и обнаруживает, что пользователь больше не вошел в систему.

Я не могу обнаружить момент сразу после того, как пользователь удалит приложение facebook из настроек, чтобы принести пользователю поток входа...

Вот мой код:

На первой сцене моего приложения...

if([FBSession activeSession].state == FBSessionStateCreatedTokenLoaded)
{
    NSLog(@"Logged in to Facebook");
    [self openFacebookSession];
    UIAlertView *alertDialog;

    alertDialog = [[UIAlertView alloc] initWithTitle:@"Facebook" message:@"You're already logged in to Facebook" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];

    [alertDialog show];

    [alertDialog release];
    return YES;
}
else{
    NSLog(@"Not logged in to Facebook"); //Show the login flow
    return NO;
}

Вот код для openFacebookSession

-(void)openFacebookSession
{
    NSArray *permissions = [[NSArray alloc] initWithObjects:
                            @"publish_stream",
                            nil];

    [FBSession openActiveSessionWithPublishPermissions:permissions defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
        [self sessionStateChanged:session state:status error:error];
    }];
}

Код для sessionStateChanged...

-(void)sessionStateChanged:(FBSession *)session state:(FBSessionState)state error:(NSError *)error
{
    switch (state) {
        case FBSessionStateOpen: {
            NSLog(@"Session opened");
        }
            break;
        case FBSessionStateClosed:
        case FBSessionStateClosedLoginFailed:
            [FBSession.activeSession closeAndClearTokenInformation];
            break;
        default:
            break;
    }

    if (error) {
        UIAlertView *alertView = [[UIAlertView alloc]
                                  initWithTitle:@"Error"
                                  message:error.localizedDescription
                                  delegate:nil
                                  cancelButtonTitle:@"OK"
                                  otherButtonTitles:nil];
        [alertView show];
    }
}

Большое спасибо!

Ответ 1

Я обнаружил, что это происходит и с самим собой... Это было обнаружено, когда пользователь изменил свой пароль в facebook, и мы больше не могли аутентифицироваться. Выполнение ручной повторной синхронизации/очистки сеанса учетной записи позволило им снова войти в систему.

-(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...
    }];
            }
        }
    }

Ответ 2

На самом деле очень сложно разобрать и обнаружить эти типы ошибок по двум причинам:

  • Я не могу найти способ обнаружить эту проблему, пока вы на самом деле не попытаетесь и не выполните FBRequest (или подобное), и
  • Объект NSError, переданный из неудавшегося FBRequest, НЕИСПРАВНО трудно разбирать и использовать любым функциональным способом.

Ниже приведен сумасшедший метод, который я написал, в то время как буквально вытащил все, чтобы справиться с этим. Он обрабатывает объект NSError *error от неудачной попытки FBRequest и передает его соответствующим методам или отображает наиболее разумную ошибку, которую я мог бы найти (или попадает в нижнюю точку).

Обратите внимание на комментарии - особенно около innerError и parsedResponse - подробно расскажите, что я обнаружил до сих пор. Удачи, храбрый солдат:

- (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);

    }
}

Ответ 3

У меня такая же проблема и не удалось найти правильную документацию о кодах ошибок на сайте SDK на Facebook.

Я решил проблему, сравнив значение [error code]; или [error userInfo];.

В вашем сеансе будет состояние FBSessionStateClosedLoginFailed, а словарь userInfo ошибки будет иметь следующую форму

"com.facebook.sdk:ErrorLoginFailedReason" = "com.facebook.sdk:ErrorLoginFailedReason";

С другой стороны, код ошибки показывает мне 2, чтобы вы могли обработать его в конце 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];
}
}