Как вы открываете FBSession с обработчиком завершения, который не сохраняется и вызывается при каждом изменении состояния сеанса?

По какой-то странной причине Facebook решил создать обработчиков завершения, которые на самом деле являются только наблюдателями уведомлений. Игнорируя, почему они это делают, мне нужно каким-то образом сделать запрос на открытие FBSession и вызвать обратный вызов только один раз, потому что объекты внутри обратного вызова НЕ МОГУТ быть сохранены.

Если пользователь нажимает кнопку, а сеанс Facebook с полномочиями «publish_actions» не открывается, тогда должен отображаться индикатор активности и предотвращать взаимодействие с пользовательским интерфейсом при открытии сеанса Facebook.

Вот метод, который используется для входа в 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», это связано с тем, что пользовательский интерфейс должен быть представлен пользователь для сбора конкретной информации, используемой для сообщений Open Graph.

2 Solutions collect form web for “Как вы открываете FBSession с обработчиком завершения, который не сохраняется и вызывается при каждом изменении состояния сеанса?”

Если вы не хотите получать изменения состояния из 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, FBSessionState status, 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, id<FBGraphUser> user, 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]); } } 

То, как я реализовал подобное поведение, – это поставить мою логику сеанса в одноэлементный. Singleton реализует обработчик завершения, который можно безопасно вызывать FB всякий раз, когда изменяется сеанс. Затем мой обработчик завершения размещает собственные уведомления для других контроллеров, чтобы наблюдать по мере необходимости.

Предположим, у вас есть синглтон MySession котором вы определили некоторые методы и уведомления вокруг FB auth. Логика контроллера вашего вида будет выглядеть примерно так:

 - (void)viewDidLoad { [super viewDidLoad]; // observe MySession notifications [MySession.sharedSession addObserver:self]; } - (void)dealloc { // stop observing MySession notifications [MySession.sharedSession removeObserver:self]; } // this is a user action that requires FB auth - (void)someActionRequiringFacebookAuth { if (![MySession.sharedSession isLoggedIntoFacebook]) { self.postAuthAction = @selector(someActionRequiringFacebookAuth); [MySession.sharedSession loginToFacebook]; } else { // perform action } } // notification posted by MySession before a FB auth attempt - (void)mySessionWillAttemptFacebookAuth:(NSNotification *)notification { [self startLoadingAnimation]; } // notification posted by MySession if a FB auth attempt succeeds - (void)mySessionDidAuthWithFacebook:(NSNotification *)notification { [self stopLoadingAnimation]; if (self.postAuthAction) { [self performSelector:self.postAuthAction]; self.postAuthAction = nil; } } // notification posted by MySession if a FB auth attempt fails - (void)mySessionFacebookAuthDidFail:(NSNotification *)notification { [self stopLoadingAnimation]; // display error if desired self.postAuthAction = nil; } 
  • Facebook SDK: знать, произошло ли совместное использование
  • Публикация диалогового окна Facebook не работает с устройством ios
  • Несогласованный сбой при отправке в facebook с использованием FBNativeDialogs на iOS6.0
  • Как управлять учетными записями производства и разработки с помощью Facebook iOS SDK
  • iOS-Fackbook - FBLoginView не показывает кнопку входа, как HelloFacebookSample
  • iOS 9 Facebook login simulator -canOpenURL: не удалось URL: «fbauth2: ///» - ошибка: «(null)»
  • Ошибки SDK для Facebook iOS - публикация открытых графических действий
  • Как я могу заставить CocoaPods использовать двоичные версии Facebook и Parse SDK?
  • Импорт учетных данных пользователя в iOS6
  • Facebook iOS SDK - приложение для Facebook падает на openActiveSessionWithReadPermissions. Мое приложение висит на входе
  • Совместное использование fb-изображения и содержимого сообщений в приложении, не работающем с использованием FBSDKShareDialog
  • FOSOAuthServerBundle - генерирует вручную токен доступа
  • PhoneC: Разработка iOS проста с помощью XCode, Swift3, UITableView, cocatouch, давайте создадим приложения для iPhone, iPad и Macbook.