6 Stimmen

Wie öffnet man eine FBSession mit einem Completion-Handler, der nicht beibehalten wird und bei jeder Sitzungsstatusänderung aufgerufen wird?

Aus irgendeinem seltsamen Grund hat Facebook beschlossen, Abschlussbehandler zu erstellen, die tatsächlich nur Benachrichtigungsbeobachter sind. Unabhängig davon, warum sie das tun, brauche ich eine Möglichkeit, eine Anfrage zum Öffnen einer FBSession zu stellen und nur einmal einen Callback auszulösen, weil die Objekte im Callback NICHT beibehalten werden dürfen.

Wenn der Benutzer eine Schaltfläche drückt und keine Facebook-Sitzung mit den Berechtigungen "publish_actions" geöffnet ist, sollte ein Aktivitätsindikator angezeigt und die Interaktion mit der Benutzeroberfläche verhindert werden, während eine Facebook-Sitzung geöffnet ist.

Hier ist die Methode, die verwendet wird, um sich bei Facebook anzumelden:

- (void)beiFacebookAnmeldenVonViewController:(UIViewController *)viewController completion:(void (^)(NSString *facebookAccountName))completion
{
    if([self istBeiFacebookAngemeldet] == 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 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]);
    }
}

Wenn der Abschlussbehandler nicht beibehalten wurde (wieder aus Gründen, die absolut keinen Sinn ergeben, weil NSNotifications gepostet werden, wenn sich die Sitzungszustände ändern), dann würde diese Methode perfekt funktionieren.

Weil die App nicht um Facebook dreht, habe ich kein Interesse daran, wann sich der Sitzungszustand ändert. Wenn der Benutzer versucht, etwas auf Facebook zu teilen und die aktive Sitzung nicht geöffnet ist, wird der Anmeldevorgang ausgelöst, und wenn eine Sitzung geöffnet ist, wird der Freigabevorgang fortgesetzt. Andernfalls wird eine Fehlermeldung dem Benutzer angezeigt.

Könnte mir jemand erklären, wie Facebook erwartet, dass diese Art von Funktionalität implementiert wird, denn es scheint sich um einen recht häufigen Anwendungsfall zu handeln, und bevor jemand fragt "warum verwenden Sie nicht die Standard-Facebook-Dialoge", liegt das daran, dass eine benutzerdefinierte Benutzeroberfläche präsentiert werden muss, um spezifische Informationen zur Verwendung bei Open Graph-Beiträgen zu sammeln.

7voto

Reid Main Punkte 3364

Wenn es Ihnen nichts ausmacht, keine Statusänderungen von FBSessionStateHandler zu erhalten, wie ich es getan habe, sollte der folgende Code es Ihnen ermöglichen, sich bei Facebook anzumelden und Ihnen einen Abschlussblock bereitzustellen, der nicht aufbewahrt wird.

Der Abschlussblock, der in die Methode übergeben wird, wird immer verwendet, wenn eine Statusänderung auftritt und dann sofort auf null gesetzt, damit, wenn eine weitere Statusänderung auftritt, diese ignoriert wird.

typedef void(^FacebookLoginCompletionBlock)(id user);

- (void)loginToFacebookFromWithCompletionBlock:(FacebookLoginCompletionBlock)completionBlock
{
    // Wenn der Benutzer nicht bei Facebook angemeldet ist, versuchen Sie, eine Sitzung mit "publish_actions"-Berechtigungen zu öffnen.
    if([[FBSession activeSession] isOpen] == NO)
    {
        // Kopieren des Abschlussblocks in eine lokale Variable, damit er nach der Verwendung auf null gesetzt werden kann, um Schleifen beizubehalten.
        __block FacebookLoginCompletionBlock copiedCompletionBlock = completionBlock;

        [FBSession openActiveSessionWithPublishPermissions:@[ @"publish_actions" ] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error)
            {
                // Führen Sie diesen Code nur aus, wenn ein Abschlussblock vorhanden ist, an den zurückgerufen werden soll. Wenn der Abschlussblock null ist, wurde er bereits verwendet, und dies ist eine Statusänderung, die uns nicht interessiert.
                if(copiedCompletionBlock != nil)
                {
                    // Da diese Methode nur darauf abzielt, dass der Benutzer sich bei Facebook anmeldet, kümmern Sie sich nur um den Öffnungszustand ohne Fehler.
                    if(status == FBSessionStateOpen && error == nil)
                    {
                        // Wenn der Benutzer sich erfolgreich bei Facebook angemeldet hat, laden Sie seine grundlegenden Profilinformationen herunter, damit die App die Informationen speichern kann, um dem Benutzer anzuzeigen, unter welchem Konto er angemeldet ist.
                        [FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id user, NSError *error)
                            {
                                if(copiedCompletionBlock != nil)
                                    copiedCompletionBlock(user);

                                // Setzen Sie den kopierten Abschlussblock auf null, damit er nicht beibehalten und jedes Mal aufgerufen wird, wenn sich der aktive FBSession-Status ändert.
                                copiedCompletionBlock = nil;
                            }];
                    }
                    // Da diese Methode nur darauf abzielt, dass der Benutzer sich bei Facebook anmeldet, rufen Sie den Abschlussblock auf, um anzuzeigen, dass ein Fehler aufgetreten ist, wenn ein anderer Status ausgelöst wird.
                    else
                    {
                        if(copiedCompletionBlock != nil)
                            copiedCompletionBlock(nil);

                        // Setzen Sie den kopierten Abschlussblock auf null, damit er nicht beibehalten und jedes Mal aufgerufen wird, wenn sich der aktive FBSession-Status ändert.
                        copiedCompletionBlock = nil;
                    }
                }

                // Dieser Block wird während der Lebensdauer der Anwendung existieren, weil Facebook aus irgendeinem seltsamen Grund den Abschluss-Handler für ihre Methoden zu offenen aktiven Sitzungen beibehält. Fügen Sie einige Codes ein, die dem Benutzer einen Fehler anzeigen, wenn Änderungen am Sitzungsstatus auftreten, von denen Facebook denkt, dass der Benutzer davon erfahren sollte. Ihr Code sollte immer überprüfen, ob eine aktive Facebook-Sitzung vorhanden ist, bevor eine Aktion ausgeführt wird, daher sollte es kein Problem sein, diese Änderungen zu ignorieren. Im schlimmsten Fall können Sie die Benachrichtigungen FBSessionDidSetActiveSessionNotification, FBSessionDidUnsetActiveSessionNotification, FBSessionDidBecomeOpenActiveSessionNotification oder FBSessionDidBecomeClosedActiveSessionNotification abhören.
                if ([error fberrorShouldNotifyUser] == YES)
                {
                    NSString *alertTitle = @"Fehler beim Einloggen in Facebook";
                    NSString *alertMessage = [error fberrorUserMessage];

                    if ([alertMessage length] == 0)
                        alertMessage = @"Bitte versuchen Sie es später erneut.";

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

                    [alertView show];
                }
            }];
    }
    // Wenn der Benutzer bereits bei Facebook angemeldet ist, rufen Sie sofort den Abschlussblock mit dem Benutzerobjekt auf, das gespeichert worden sein sollte, als sich der Benutzer zuvor angemeldet hat.
    else
    {
        if(completionBlock != nil)
            completionBlock([self facebookUser]);
    }
}

1voto

XJones Punkte 21918

Die Art und Weise, wie ich ein ähnliches Verhalten implementiert habe, besteht darin, meine Sitzungslogik in einem Singleton zu platzieren. Das Singleton implementiert einen Abschluss-Handler, der sicher von FB aufgerufen werden kann, wenn sich die Sitzung ändert. Mein Abschluss-Handler sendet dann seine eigenen Benachrichtigungen, die von anderen Controllern bei Bedarf beobachtet werden können.

Angenommen, Sie haben ein MySession Singleton, in dem Sie einige Methoden und Benachrichtigungen rund um die FB-Authentifizierung definiert haben. Ihre ViewController-Logik würde ungefähr so aussehen:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // beobachten MySession Benachrichtigungen
    [MySession.sharedSession addObserver:self];
}

- (void)dealloc
{
    // höre auf, MySession-Benachrichtigungen zu beobachten
    [MySession.sharedSession removeObserver:self];
}

// dies ist eine Benutzeraktion, die eine FB-Authentifizierung erfordert
- (void)someActionRequiringFacebookAuth
{
    if (![MySession.sharedSession isLoggedIntoFacebook]) {
        self.postAuthAction = @selector(someActionRequiringFacebookAuth);
        [MySession.sharedSession loginToFacebook];
    }

    else {
        // Aktion durchführen
    }
}

// Benachrichtigung, die von MySession vor einem FB-Authentifizierungsversuch veröffentlicht wird
- (void)mySessionWillAttemptFacebookAuth:(NSNotification *)notification
{
    [self startLoadingAnimation];
}

// Benachrichtigung, die von MySession veröffentlicht wird, wenn ein FB-Authentifizierungsversuch erfolgreich ist
- (void)mySessionDidAuthWithFacebook:(NSNotification *)notification
{
    [self stopLoadingAnimation];
    if (self.postAuthAction) {
        [self performSelector:self.postAuthAction];
        self.postAuthAction = nil;
    }
}

// Benachrichtigung, die von MySession veröffentlicht wird, wenn ein FB-Authentifizierungsversuch fehlschlägt
- (void)mySessionFacebookAuthDidFail:(NSNotification *)notification
{
    [self stopLoadingAnimation];
    // Fehlermeldung anzeigen, wenn gewünscht
    self.postAuthAction = nil;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X