ios – 如何使用完成处理程序打开FBSession,该处理程序不会被保留并在每次会话状态更改时调用?

出于某些奇怪的原因,Facebook决定创建完成处理程序,实际上只是通知观察者.忽略他们为什么这样做,我需要一些方法来发出一个请求打开一个FBSession并且回调只发生一次,因为回调中的对象不能被保留.

如果用户按下按钮并且具有“publish_actions”权限的Facebook会话未打开,则应当显示活动指示符并且在打开Facebook会话时阻止与UI的交互.

以下是用于登录Facebook的方法:

- (void)loginToFacebookFromViewController:(UIViewController *)viewController completion:(void (^)(NSString *facebookAccountName))completion
{
    if([self isLoggedIntoFacebook] == NO)
    {
        [viewController startLoadingAnimation];

        [FBSession openActiveSessionWithPublishPermissions: @[ @"publish_actions" ] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:^(FBSession *session,FBSessionState status,NSError *error)
            {
                if(error == nil)
                {
                    [FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection,id<FBGraphUser> user,NSError *error)
                        {
                            NSString *accountName = [user name];

                            if(error == nil)
                            {
                                NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
                                [standardUserDefaults setObject:accountName forKey:TSFacebookAccountName];
                                [standardUserDefaults synchronize];
                            }

                            [viewController stopLoadingAnimation];

                            if(completion != nil)
                                completion(accountName);
                        }];
                }
                else
                {
                    [viewController stopLoadingAnimation];

                    if(completion != nil)
                        completion(nil);
                }
            }];
    }
    else
    {
        if(completion != nil)
            completion([self facebookAccountName]);
    }
}

如果没有保留完成处理程序(再次出于不会因为在会话状态发生变化时发布NSNotifications而没有意义的原因),那么这种方法可以完美地工作.

因为应用程序不围绕Facebook,我对会话状态何时发生变化没有兴趣.如果用户尝试与Facebook共享内容并且活动会话未打开,则将启动登录过程,如果会话已打开,则共享过程将继续.否则,将向用户显示错误消息.

有人可以解释一下Facebook是如何实现这种功能的,因为它似乎是一个非常常见的用例,在有人提到“你为什么不使用默认的Facebook对话框”之前,这是因为需要将自定义UI呈现给用户收集用于Open Graph帖子的特定信息.

解决方法

如果您不关心从FBSessionStateHandler接收状态更改,那么下面的代码应该允许您登录到Facebook并为您提供未保留的完成块.

传递给方法的完成块将始终在发生任何状态更改时使用,然后立即被清除,以便在发生另一个状态更改时忽略它.

typedef void(^FacebookLoginCompletionBlock)(id<FBGraphUser> user);

- (void)loginToFacebookFromWithCompletionBlock:(FacebookLoginCompletionBlock)completionBlock
{
    // If the user is not logged into Facebook attempt to open a session with "publish_actions" permissions.
    if([[FBSession activeSession] isOpen] == NO)
    {
        // Copy the completion block to a local variable so it can be nil'd out after it is used to prevent retain loops.
        __block FacebookLoginCompletionBlock copiedCompletionBlock = completionBlock;

        [FBSession openActiveSessionWithPublishPermissions:@[ @"publish_actions" ] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:^(FBSession *session,NSError *error)
            {
                // Only attempt to run any of this code if there is a completion block to call back to. If completion block is nil than it has already been used and this is a state change that we do not care about.
                if(copiedCompletionBlock != nil)
                {
                    // Because this method is only concerned with the user logging into Facebook just worry about the open state occuring with no errors.
                    if(status == FBSessionStateOpen && error == nil)
                    {
                        // If the user successfully logged into Facebook download their basic profile information so the app can save the information to display to the user what account they are logged in under.
                        [FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection,NSError *error)
                            {
                                if(copiedCompletionBlock != nil)
                                    copiedCompletionBlock(user);

                                // nil out the copied completion block so it is not retained and called everytime the active FBSession's state changes.
                                copiedCompletionBlock = nil;
                            }];
                    }
                    // Because this method is only concerned with the user logging into Facebook if any other state is triggered call the completion block indicating that there was a failure.
                    else
                    {
                        if(copiedCompletionBlock != nil)
                            copiedCompletionBlock(nil);

                        // nil out the copied completion block so it is not retained and called everytime the active FBSession's state changes.
                        copiedCompletionBlock = nil;
                    }
                }

                // This block will exist the lifespan of the application because for some bizarre reason Facebook retains the completion handler for their open active session methods. Throw in some code that will display an error to the user if any session state changes occur that Facebook thinks the user should be aware of. Your code should be always checking if a active Facebook session exists before taking any action so not being aware of these changes should not be any issue. Worst case scenario you can listen for FBSessionDidSetActiveSessionNotification,FBSessionDidUnsetActiveSessionNotification,FBSessionDidBecomeOpenActiveSessionNotification or FBSessionDidBecomeClosedActiveSessionNotification notifications.
                if ([error fberrorShouldNotifyUser] == YES)
                {
                    NSString *alertTitle = @"Error logging into Facebook";
                    NSString *alertMessage = [error fberrorUserMessage];

                    if ([alertMessage length] == 0)
                        alertMessage = @"Please try again later.";

                    UIAlertView *alertView =  [[UIAlertView alloc] initWithTitle:alertTitle message:alertMessage delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

                    [alertView show];
                }
            }];
    }
    // If the user is already logged into Facebook immediately call the completion block with the user object that should have been saved when the user previously logged in.
    else
    {
        if(completionBlock != nil)
            completionBlock([self facebookUser]);
    }
}

以上是来客网为你收集整理的ios – 如何使用完成处理程序打开FBSession,该处理程序不会被保留并在每次会话状态更改时调用?全部内容,希望文章能够帮你解决ios – 如何使用完成处理程序打开FBSession,该处理程序不会被保留并在每次会话状态更改时调用?所遇到的程序开发问题。

如果觉得来客网网站内容还不错,欢迎将来客网网站推荐给程序员好友。