Любой способ иметь синтаксис стиля дополненияHandler, используя [авторизацию facebook] на iOS 5? - PullRequest
1 голос
/ 05 марта 2012

Приведенный ниже код пытается лениво войти в Facebook прямо перед публикацией фотографии, но имеет асинхронную проблему.В журналах after isSessionValid block появится до fbDidLogin, а затем произойдет facebookErrDomain error 10000 («OAuthException», «должен использоваться активный токен доступа» и т. Д.).

MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
if (![appDelegate.facebook isSessionValid]) {
    [appDelegate.facebook authorize:[NSArray arrayWithObjects:@"publish_stream", @"user_photos", nil]];
}

NSLog(@"after isSessionValid block");

NSData *imageData = UIImageJPEGRepresentation(image, 1);
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                               FACEBOOK_APP_ID, @"app_id",
                               imageData, @"source",
                               message,  @"message",
                               nil];

[appDelegate.facebook requestWithGraphPath:@"me/photos" andParams:params andHttpMethod:@"POST" andDelegate:self];

Здесьэто fbDidLogin в MyAppDelegate

- (void)fbDidLogin {
    NSLog(@"fbDidLogin");

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:[facebook accessToken] forKey:@"FBAccessTokenKey"];
    [defaults setObject:[facebook expirationDate] forKey:@"FBExpirationDateKey"];
    [defaults synchronize];
}

Я понимаю, что facebook requestWithGraphPath работает до fbDidLogin на FBSessionDelegate, но не уверен, что лучший способ получить код под after isSessionValid block оператор лога, и он будет работать внутри fbDidLogin?

Вопрос

Мне бы хотелось иметь API в стиле completionHandler, как показано ниже.Есть ли простой способ сделать это?В качестве альтернативы, есть ли хороший способ добавить обратный вызов или блок к MyAppDelegate, который будет вызван один раз из fbDidLogin, а затем удален?

[appDelegate.facebook authorize:array completionHandler:^(BOOL success) {
    // other setup stuff from first example
    [appDelegate.facebook requestWithGraphPath:@"me/photos" andParams:params andHttpMethod:@"POST" andDelegate:self];
}];

Обновление

Ответ на Как реагировать на асинхронные события (логин)? может быть то, что я ищу.

Ответы [ 3 ]

3 голосов
/ 06 марта 2012

Одна из возможностей - использовать семафор GCD и поместить requestWithGraph: в фоновую очередь.

Добавить свойство к MyAppDelegate для хранения семафора; создайте его с помощью dispatch_semaphore_create, передав 1, потому что вы имеете дело с двумя потоками - вы хотите, чтобы только один мог работать одновременно:

@property (assign) dispatch_semaphore_t authSemaphore;

authSemaphore = dispatch_semaphore_create(1);

Затем уменьшите семафор прямо перед авторизацией:

if (![appDelegate.facebook isSessionValid]) {
    dispatch_semaphore_wait([appDelegate authSemaphore], DISPATCH_TIME_FOREVER);
    [appDelegate.facebook authorize:[NSArray arrayWithObjects:@"publish_stream", @"user_photos", nil]];
}

И сигнализировать, когда авторизация прошла успешно:

- (void)fbDidLogin {
    //...
    [defaults synchronize];
    dispatch_semaphore_signal([self authSemaphore]);
}

Теперь вы можете подождать на семафоре прямо перед тем, как попытаться опубликовать изображение. Если авторизация выполняется, ожидающий вызов будет заблокирован, и следующий код не будет выполняться, пока семафор не будет сигнализирован. Однако, если семафор доступен, код будет работать нормально.

Вы должны поместить это в фоновую очередь, чтобы избежать блокировки основного потока, но вам не нужно создавать новый; одна из глобальных параллельных очередей должна работать нормально:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait([appDelegate authSemaphore], DISPATCH_TIME_FOREVER);
    [appDelegate.facebook requestWithGraphPath:@"me/photos" andParams:params andHttpMethod:@"POST" andDelegate:self];
    dispatch_semaphore_signal([appDelegate authSemaphore]);
});
3 голосов
/ 06 марта 2012

Вот то, что я положил в MyAppDelegate, что спасает завершениеHandler и вызывает либо с YES, либо с NO в зависимости от входа в систему, затем устанавливает для завершенияHHDLER nil. Я уверен, что я должен поместить туда @try / @catch где-нибудь, чтобы завершитьHandler установить в ноль.

@interface MyAppDelegate : UIResponder <UIApplicationDelegate, FBSessionDelegate>
{
    void (^_completionHandler)(BOOL success);
}

- (void)facebookAuthorizeWithCompletionHandler:(void (^)(BOOL success))completionHandler {
    if (![facebook isSessionValid]) {
        _completionHandler = completionHandler;
        [facebook authorize:[NSArray arrayWithObjects:@"publish_stream", @"user_photos", nil]];
    } else {
        completionHandler(YES);
    }
}

- (void)fbDidLogin {
    NSLog(@"fbDidLogin");
    // removed code that saves accessToken/expirationDate to NSUserDefaults

    if (_completionHandler) {
        _completionHandler(YES);
        _completionHandler = nil;
    }
}

- (void)fbDidNotLogin:(BOOL)cancelled {
    NSLog(@"fbDidNotLogin");

    if (_completionHandler) {
        _completionHandler(NO);
        _completionHandler = nil;
    }
}

Я обратился к нему, используя подобный код, который, кажется, работает (но я хочу перечитать о Блоков и переменных , чтобы убедиться, что я понимаю проблемы с управлением памятью).

[appDelegate facebookAuthorizeWithCompletionHandler:^(BOOL success) {

    NSLog(@"in completionHandler, success=%d", success);

    if (success) {
        // other setup stuff from first example
        [appDelegate.facebook requestWithGraphPath:@"me/photos" andParams:params andHttpMethod:@"POST" andDelegate:self];
    }
}];
2 голосов
/ 05 марта 2012

Вы можете написать расширение для объекта Facebook, использовать вторичный объект (экземпляр другого класса), который регистрируется в качестве делегата и сохраняет обработчик.Вы можете использовать ассоциативные ссылки , чтобы сохранить этот объект в экземпляре Facebook.Таким образом, когда выполняются методы делегата, этот вторичный объект может просто выполнить блок обработчика.

В этом проекте есть много примеров этого (NSURLConnection-BKAdditions.m дляпример) Здесь у вас есть еще одна ссылка , которая может помочь вам с ассоциативными ссылками.

...