Заполнение моей модели данными Facebook (‹FBGraphUser›)

Я создал модель под названием «Пользователь», у которой есть свойство «имя».

Я делаю запрос к facebook API (используя последнюю версию SDK для iOS), идея заключается в том, чтобы установить мое свойство user.name, когда facebook возвращает данные.

Facebook возвращает данные в блок startWithCompletionHandler, но я не могу установить эти данные в свой пользовательский объект, я могу получить доступ только к данным в startWithCompletionHandler. Когда я пытаюсь получить доступ к данным из блока, моя модель возвращает NULL.

Как я могу заполнить свою модель/объект, когда возвращается запрос facebook?

Моя текущая реализация метода:

+ (void)requestUserData:(User *)userModel {
    if(FBSession.activeSession.isOpen) {
     [[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
       if(user.name) [self setUserData:user userModel:userModel];
     }];
   }
}

 + (void)setUserData:(NSDictionary<FBGraphUser> *)user userModel:(User *)userModel {
     userModel.name = user.name;
 }

И звонок:

   __block User *user = [[User alloc] init];
   [GFFacebookHelpers requestUserData:user];
   NSLog(@"user: %@", user.name); //this part prints 2013-06-06 18:03:43.731 FacebookClassTest[74172:c07] user: (null)

Спасибо.


person GodFather    schedule 06.06.2013    source источник
comment
Вы пытаетесь сопоставить данные FBGraphUser с вашей моделью или у вас просто есть свойство name в вашей модели?   -  person Daniel    schedule 07.06.2013
comment
В настоящее время для теста у меня есть только свойство name, но после того, как этот вопрос будет решен, я расширим свою модель, чтобы хранить основные данные пользователя facebook.   -  person GodFather    schedule 07.06.2013


Ответы (1)


SDK Facebook загружается асинхронно, поэтому написанный вами обработчик завершения выполняется после возврата вашего вспомогательного метода.

Поэтому, когда ваш метод возвращает ваш объект User, логически это просто пустой пользователь (поскольку вы уже выделили/инициализировали его).

Когда вызывается блок завершения, этот пользовательский объект обновляется, но к этому времени вы больше не обрабатываете его в своем приложении (контроллере представления).

Итак, вот мое предложение: либо используйте SDK Facebook более непосредственно из ваших контроллеров представления, либо, если вы хотите сохранить всю эту логику в своем вспомогательном классе, измените ее, чтобы ваш метод не возвращал данные, но для него требуется блок, который будет вызван после завершения запроса данных, что-то вроде этого:

+ (void)requestUserDataWithCompletionHandler:(void (^)(User *user, NSError *error))handler{
    if(FBSession.activeSession.isOpen) {        
        [[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
            if(!error){
                User *userData = [[User alloc] init];
                userData.name = user.name;

                dispatch_async(dispatch_get_main_queue(), ^{
                    handler(userData, nil);
                });
            }
            else{
                dispatch_async(dispatch_get_main_queue(), ^{
                    handler(nil, error);
                });
            }
        }];
    }
}

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

Метод вашего контроллера представления теперь будет выглядеть так:

- (void)updateStatusLabels {
    if([GFFacebookHelpers isLogged]){
        [GFFacebookHelpers requestUserDataWithCompletionHandler:^(User *user, NSError *error) {
            if(!error){
                self.fbStatus.text = user.name;
                [_loginButton setTitle:@"Logout" forState:UIControlStateNormal];
            }
        }];
    }else{
        self.fbStatus.text = @"You need to login";

        [_loginButton setTitle:@"Login" forState:UIControlStateNormal];
    }
}
person Daniel    schedule 06.06.2013
comment
Дэниел, я допустил ошибку в своем примере фрагмента кода. Я устанавливаю userModel.name таким образом: userModel.name = user.name без цикла. Я исправил это в майском посте. Я уже реализовал код вашего предложения, но продолжаю получать NULL при вызове __block User *user = [[User alloc] init]; [GFFacebookHelpers requestUserData:user]; NSLog(@"user: %@", user.name); - person GodFather; 07.06.2013
comment
Если у вас есть демонстрационный проект, который я могу скачать, я буду рад рассмотреть его поближе. - person Daniel; 07.06.2013
comment
Даниэль, я поместил демонстрацию, где я работаю, здесь: git://github.com/godfather/facebookclasstest.git оставайтесь свободными, чтобы изменить то, что вы хотите. PS.: Я сделал некоторые изменения после последнего примера, который я разместил здесь. Спасибо. - person GodFather; 07.06.2013
comment
Я разветвил ваш репозиторий, кое-что изменил и создал для вас запрос на включение. В основном ваш блок завершения запроса запускается после возврата метода, потому что Facebook SDK асинхронно извлекает данные. Поэтому я добавил блок обратного вызова в ваш метод. Теперь это похоже на обертку над оболочкой... но, по крайней мере, вы можете обрабатывать ошибки данных во вспомогательном классе и нести ответственность за отображение/скрытие частей вашего приложения в соответствии с успехом/неудачей. Пожалуйста, взгляните на свое репо. Я также обновлю здесь свой ответ. - person Daniel; 07.06.2013