Facebook iOS SDK 3.5.1: openActiveSessionWithReadPermissions - completion handler called twice

10,750

Solution 1

It appears that by design, Facebook SDK retains references to block handlers, even after they have been called. Thus, in your call to openActiveSessionWithReadPermissions the completion handler may be called numerous times, in case the session state changes. See Facebooks comment on this issue here.

As a work around, you might want to implement your own mechanism that ensures the handler is fired only once:

__block FBSessionStateHandler runOnceHandler = ^(FBSession *session,
                                             FBSessionState status,
                                             NSError *error) { /* YOUR CODE HERE */ };

...

 [FBSession openActiveSessionWithReadPermissions:YOUR_PERMISSIONS
                                       allowLoginUI:YES
                                  completionHandler:^(FBSession *session,
                                                      FBSessionState status,
                                                      NSError *error) {
                                      if (runOnceHandler) {
                                          runOnceHandler(session, status, error);
                                          runOnceHandler = nil;
                                      }

                                  }
     ];

Solution 2

You Can use this

- (IBAction)facebookBasti:(id)sender {
if(FBSession.activeSession.isOpen){

    [[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
        if (!error) {

            NSLog(@" Email = %@",[user objectForKey:@"email"]);
        }
    }];

    NSLog(@"POST TO WALL -- %@",FBSession.activeSession.accessToken);
    [self publishFacebook];

}
else {
    // try to open session with existing valid token
    NSArray *permissions = [[NSArray alloc] initWithObjects:
                            @"publish_actions",@"email",
                            nil];
    FBSession *session = [[FBSession alloc] initWithPermissions:permissions];
    [FBSession setActiveSession:session];
    if([FBSession openActiveSessionWithAllowLoginUI:NO]) {
        // post to wall
        [[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
            if (!error) {

                NSLog(@" Email = %@",[user objectForKey:@"email"]);
            }
        }];

        NSLog(@"POST TO WALL -- %@",FBSession.activeSession.accessToken);
        [self publishFacebook];
    } else {
        // you need to log the user
        NSLog(@"login");

        [FBSession openActiveSessionWithPermissions:permissions
                                       allowLoginUI:YES
                                  completionHandler:^(FBSession *session,
                                                      FBSessionState state,
                                                      NSError *error) {
                                      NSLog(@"POST TO WALL -- %@",FBSession.activeSession.accessToken);
                                      [self publishFacebook];

                                  }];
    }
}

}

and publishFacebook method

   -(void)publishFacebook
   {
NSMutableDictionary *postParams2= [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                                   haberLink, @"link",
                                   @"abc.com", @"name",
                                   title, @"caption",
                                   desc, @"description",
                                   nil];

[FBRequestConnection
 startWithGraphPath:@"me/feed"
 parameters:postParams2
 HTTPMethod:@"POST"
 completionHandler:^(FBRequestConnection *connection,
                     id result,
                     NSError *error) {
     NSString *alertText;
     if (error) {
         alertText = [NSString stringWithFormat:
                      @"error: domain = %@, code = %d",
                      error.domain, error.code];
     } else {
         alertText = [NSString stringWithFormat: @"Shared Facebook"];



         [[[UIAlertView alloc] initWithTitle:@"Shared Facebook"
                                     message:alertText
                                    delegate:self
                           cancelButtonTitle:@"Ok"
                           otherButtonTitles:nil]
          show];

     }
 }];

}

Solution 3

Please read Upgrading from 3.0 to 3.1, in particular the paragraph Asking for Read & Write Permissions Separately. It seems like Facebook SDK is not meant to be used this way.

You are now required to request read and publish permission separately (and in that order). Most likely, you will request the read permissions for personalization when the app starts and the user first logs in. Later, if appropriate, your app can request publish permissions when it intends to post data to Facebook.

and

It is important that you do not simply attempt to call the two individual methods back-to-back to replace either of the deprecated functions.

I wonder how you managed to solve this issue. BTW, I get the same crash report (FBSession: It is not valid to reauthorize while a previous reauthorize call has not yet completed).

Share:
10,750
laucel
Author by

laucel

Updated on June 15, 2022

Comments

  • laucel
    laucel almost 2 years

    I have a button to share a link. I'm using basically two calls: openActiveSessionWithReadPermissions and requestNewPublishPermissions.

    So this is the button action:

    - (IBAction) shareFacebookButtonAction:(id)sender
    if (![[FBSession activeSession] isOpen])
            {
                NSArray *permissions = @[@"read_friendlists", @"email"];
                [FBSession openActiveSessionWithReadPermissions:permissions
                                                   allowLoginUI:YES
                                              completionHandler:^(FBSession *session,
                                                                  FBSessionState state,
                                                                  NSError *error)
                 {
                     if (FB_ISSESSIONOPENWITHSTATE([session state]))
                     {
                         [self _prepareShare];
                     }
                     else
                     {
                         // show alert view with error
                     }
                 }];
            }
            else
            {        
                [self _prepareShare];
            }
        }
    

    and with this I'm asking for publish permission, if no permissione are found in session

    -(void) _prepareShare;
    {
        if ([FBSession.activeSession.permissions
             indexOfObject:@"publish_actions"] == NSNotFound)
        {
            [FBSession.activeSession
             requestNewPublishPermissions:
             [NSArray arrayWithObject:@"publish_actions"]
             defaultAudience:FBSessionDefaultAudienceFriends
             completionHandler:^(FBSession *session, NSError *error)
             {
                 if (!error)
                 {
                     [self _share];
                 }
                 else
                 {
                     //error
                 }
             }];
        } else
        {
           [self _share];
        }
    }
    

    _share just posts something

    -(void) _share;
    {
    
        NSMutableDictionary *params_dict = [NSMutableDictionary dictionary];
        // setting some params
    
        [FBRequestConnection startWithGraphPath:@"me/feed" parameters:params_dict HTTPMethod:@"POST" completionHandler:^(FBRequestConnection *connection, id result, NSError *error)
        {
            if (result)
            {
                // sharing succedeed, do something
            }
            else if (error)
            {
                //sharing failed, do something else
            }
        }];
    }
    

    First time I try to share (already logged on FB in iOS6 and app already authorized) completion handler of openActiveSessionWithReadPermissions is being called twice: once with FBSessionStateOpen and once with FBSessionStateOpenTokenExtended (from the openSessionForPublishPermissions call). As a consequence, _share is also called twice, first time in the else part of _prepareShare (if I already have publish permissions) and the second time in the completion handler of openSessionForPublishPermissions. So I have a double post on Facebook wall, just the first time I ever share in the app. I also had a crash report for FBSession: It is not valid to reauthorize while a previous reauthorize call has not yet completed (I couldn't be able to make it happen again).

    What is the proper way to handle this situation?

  • laucel
    laucel almost 11 years
    thanks for your help, but I would really want to understand if I'm doing something wrong and the proper way to handle this Facebook behavior
  • Alihan
    Alihan almost 11 years
    in your prepare share method you have already post it when you call _share method post it again
  • laucel
    laucel almost 11 years
    yes, but one is in if block (if I don't have publish permission, ask for it, then share), the other one is in the else block (if I already have publish permission, just share). The problem is that _share is called in the else statement of _prepareshare triggered by FBSessionStateOpenTokenExtended , while requestNewPublishPermissions hasn't called yet its completion handler, when _share is called again