Facebook Graph API v2.0 + -/me/friends возвращает пустое место или только друзья, которые также используют мое приложение

Я пытаюсь получить имя моего друга и идентификаторы с помощью Graph API версии 2.0, но данные возвращают пустой:

{
  "data": [
  ]
}

Когда я использовал v1.0, все было в порядке со следующим запросом:

FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
                                              NSDictionary* result,
                                              NSError *error) {
    NSArray* friends = [result objectForKey:@"data"];
    NSLog(@"Found: %i friends", friends.count);
    for (NSDictionary<FBGraphUser>* friend in friends) {
        NSLog(@"I have a friend named %@ with id %@", friend.name, friend.id);
    }
}];

Но теперь я не могу подружиться!

Ответ 1

В v2.0 API-интерфейса, вызывающий /me/friends возвращает друзей человека, которые также используют это приложение.

Кроме того, в версии 2.0 вы должны запросить разрешение user_friends от каждого пользователя. user_friends больше не включается по умолчанию во всех входах. Каждый пользователь должен предоставить разрешение user_friends, чтобы появиться в ответе /me/friends. См. руководство по обновлению Facebook для получения более подробной информации или просмотрите сводку ниже.

Если вы хотите получить доступ к списку друзей, не использующих приложение, есть два варианта:

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

Для приложений, которые хотят, чтобы люди приглашали друзей использовать приложение, вы все равно можете использовать Отправить диалог в Интернете или новый Диалог сообщений в iOS и Android.

UPDATE: Facebook опубликовал FAQ по этим изменениям здесь: https://developers.facebook.com/docs/apps/faq, в котором объясняются все варианты, доступные разработчикам, чтобы приглашать друзей и т.д.

Ответ 2

Хотя ответ Саймона Кросса принят и правилен, я подумал, что я немного подгоняю пример (андроид) того, что нужно сделать. Я сохраню это как общее, насколько смогу, и сосредоточусь только на этом вопросе. Лично я запустил хранение вещей в базе данных, поэтому загрузка была плавной, но для этого требуется CursorAdapter и ContentProvider, который немного отличается от области.

Я сам пришел сюда, а потом подумал, что теперь?!

Проблема

Как и user3594351, я заметил, что данные друзей были пустыми. Я нашел это, используя FriendPickerFragment. То, что работало три месяца назад, больше не работает. Даже примеры на facebook сломались. Итак, моя проблема: "Как создать FriendPickerFragment вручную?

Что не работает

Вариант № 1 от Саймона Кросса был недостаточно силен, чтобы приглашать друзей в приложение. Саймон Кросс также рекомендовал диалоговое окно "Запросы", но это обеспечило бы только 5 запросов одновременно. В диалоговом окне "Запросы" также показывались одни и те же друзья во время любого сеанса, записанного в Facebook. Не полезно.

Что работало (резюме)

Вариант №2 с некоторой тяжелой работой. Вы должны убедиться, что выполняете новые правила Facebook: 1.) Вы игра 2.) У вас есть приложение Canvas (веб-присутствие). 3.) Приложение зарегистрировано в Facebook. Все сделано на веб-сайте разработчика Facebook в настройках.

Чтобы подражать помощнику друзей вручную в моем приложении, я сделал следующее:

  • Создайте вкладку, которая показывает два фрагмента. Каждый фрагмент показывает список. Один фрагмент для доступного друга (/me/friends) и другого для приглашенных друзей (/me/invitable_friends). Используйте один и тот же фрагмент кода для отображения обеих вкладок.
  • Создайте AsyncTask, который получит данные друга из Facebook. После загрузки этих данных переместите его на адаптер, который отобразит значения на экране.

Подробнее

AsynchTask

private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {
        private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
        GraphObject graphObject;
        ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();

        @Override
        protected Boolean doInBackground(FacebookFriend.Type... pickType) {
            //
            //Determine Type
            //
            String facebookRequest;
            if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
                facebookRequest = "/me/friends";
            } else {
                facebookRequest = "/me/invitable_friends";
            }

            //
            //Launch Facebook request and WAIT.
            //
            new Request(
                    Session.getActiveSession(),
                    facebookRequest,
                    null,
                    HttpMethod.GET,
                    new Request.Callback() {
                        public void onCompleted(Response response) {
                            FacebookRequestError error = response.getError();
                            if (error != null && response != null) {
                                Log.e(TAG, error.toString());
                            } else {
                                graphObject = response.getGraphObject();
                            }
                        }
                    }
            ).executeAndWait();

            //
            //Process Facebook response
            //
            //
            if (graphObject == null) {
                return false;
            }

            int numberOfRecords = 0;
            JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
            if (dataArray.length() > 0) {

                // Ensure the user has at least one friend ...
                for (int i = 0; i < dataArray.length(); i++) {

                    JSONObject jsonObject = dataArray.optJSONObject(i);
                    FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);

                    if (facebookFriend.isValid()) {
                        numberOfRecords++;

                        myList.add(facebookFriend);
                    }
                }
            }

            //make sure there are records to process
            if (numberOfRecords > 0){
                return true;
            } else {
                return false;
            }
        }

        @Override
        protected void onProgressUpdate(Boolean... booleans) {
            //no need to update this, wait until the whole thread finishes.
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if (result) {
                /*
                User the array "myList" to create the adapter which will control showing items in the list.
                 */

            } else {
                Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
            }
        }
    }

Созданный мной класс FacebookFriend

public class FacebookFriend {
    String facebookId;
    String name;
    String pictureUrl;
    boolean invitable;
    boolean available;
    boolean isValid;
    public enum Type {AVAILABLE, INVITABLE};

    public FacebookFriend(JSONObject jsonObject, Type type) {
        //
        //Parse the Facebook Data from the JSON object.
        //
        try {
            if (type == Type.INVITABLE) {
                //parse /me/invitable_friend
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");

                //Handle the picture data.
                JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
                boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
                if (!isSilhouette) {
                    this.pictureUrl = pictureJsonObject.getString("url");

                } else {
                    this.pictureUrl = "";
                }

                this.invitable = true;
            } else {
                //parse /me/friends
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");
                this.available = true;
                this.pictureUrl = "";
            }

            isValid = true;
        } catch (JSONException e) {
            Log.w("#", "Warnings - unable to process FB JSON: " + e.getLocalizedMessage());
        }
    }
}

Ответ 3

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

Вы сможете вывести список друзей, которые только разрешили это приложение.

Приложения, использующие Graph API 1.0, будут работать до 30 апреля 2015 г., после чего они будут устаревать.

См. ссылку ниже, чтобы получить более подробную информацию об этом

https://developers.facebook.com/docs/graph-api/reference/v2.2/user/friends

https://developers.facebook.com/docs/apps/faq#invite_to_app

Надеюсь, что это поможет

Ответ 4

Как отметил Саймон, это невозможно в новом API facebook. Чисто технически говоря вы можете сделать это через автоматизацию браузера.

  • Это противоречит политике Facebook, поэтому в зависимости от страны, в которой вы проживаете, это может быть не законным.
  • вам придется использовать ваши учетные данные/запрашивать у пользователя учетные данные и, возможно, хранить их (хранение паролей даже симметрично зашифровано не является хорошей идеей)
  • когда facebook изменяет свой api, вам также придется обновлять код автоматизации браузера (если вы не можете принудительно обновлять приложение, вы должны включить кусок автоматизации браузера как webservice)
  • это обходит концепцию OAuth
  • С другой стороны, я чувствую, что владею своими данными, включая список моих друзей, и FB не должен ограничивать доступ к ним через API

Пример реализации с использованием WatiN

class FacebookUser
{
  public string Name { get; set; }
  public long Id { get; set; }
}

public IList<FacebookUser> GetFacebookFriends(string email, string password, int? maxTimeoutInMilliseconds)
{
  var users = new List<FacebookUser>();
  Settings.Instance.MakeNewIeInstanceVisible = false;
  using (var browser = new IE("https://www.facebook.com"))
  {
    try
    {
      browser.TextField(Find.ByName("email")).Value = email;
      browser.TextField(Find.ByName("pass")).Value = password;
      browser.Form(Find.ById("login_form")).Submit();
      browser.WaitForComplete();
    }
    catch (ElementNotFoundException)
    {
      // we're already logged in
    }
    browser.GoTo("https://www.facebook.com/friends");
    var watch = new Stopwatch();
    watch.Start();

    Link previousLastLink = null;
    while (maxTimeoutInMilliseconds.HasValue && watch.Elapsed.TotalMilliseconds < maxTimeoutInMilliseconds.Value)
    {
      var lastLink = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
&& l.GetAttributeValue("data-hovercard").Contains("user.php")
&& l.Text != null
).LastOrDefault();
      if (lastLink == null || previousLastLink == lastLink)
      {
        break;
      }

      var ieElement = lastLink.NativeElement as IEElement;
      if (ieElement != null)
      {
        var htmlElement = ieElement.AsHtmlElement;
        htmlElement.scrollIntoView();
        browser.WaitForComplete();
      }

      previousLastLink = lastLink;
    }

    var links = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
      && l.GetAttributeValue("data-hovercard").Contains("user.php")
      && l.Text != null
    ).ToList();

    var idRegex = new Regex("id=(?<id>([0-9]+))");
    foreach (var link in links)
    {
      string hovercard = link.GetAttributeValue("data-hovercard");
      var match = idRegex.Match(hovercard);
      long id = 0;
      if (match.Success)
      {
        id = long.Parse(match.Groups["id"].Value);
      }
      users.Add(new FacebookUser
      {
        Name = link.Text,
        Id = id
      });
    }
  }
  return users;
}

Прототип с реализацией этого подхода (с использованием С#/Watin) см. https://github.com/svejdo1/ShadowApi Это также позволяет динамическое обновление соединителя facebook, которое извлекает список ваших контактов.

Ответ 6

В Swift 4.2 и Xcode 10.1

Если вы хотите получить список друзей от FB, вам нужно отправить свое приложение на рассмотрение в FB. Посмотрите некоторые разрешения на вход.

https://developers.facebook.com/docs/apps/review/login-permissions

Здесь два шага

1) Сначала статус вашего приложения должен быть в Live

2) Получить необходимые разрешения формы FB.

1) Включите статус нашего приложения в режиме реального времени:

  1. Перейдите на страницу приложений и выберите свое приложение

https://developers.facebook.com/apps/

  1. Выберите статус в правом верхнем углу в Dashboard.

enter image description here

  1. Отправить URL политики конфиденциальности

enter image description here

  1. Выберите категорию

enter image description here

  1. Теперь наше приложение в живом состоянии.

enter image description here

На шаге завершено.

2) Отправьте наше приложение на рассмотрение:

  1. Сначала отправьте необходимые запросы.

Например: user_friends, user_videos, user_posts и т.д.

enter image description here

  1. Второй перейти на страницу текущего запроса

enter image description here

Пример: user_events

  1. Отправить все детали

enter image description here

  1. Как это отправить для всех запросов (user_friends, user_events, user_videos, user_posts и т.д.).

  2. Наконец, отправьте ваше приложение на рассмотрение.

Если ваш отзыв принял форму FB, теперь вы имеете право читать контакты и т.д.

Ответ 7

в FBSDKGraphAPI версии 2.0 или выше, вы должны запросить разрешение user_friends от каждого пользователя во время входа в систему FB. Поскольку user_friends больше не включается по умолчанию в каждый логин, мы должны добавить это. Каждый пользователь должен предоставить разрешение user_friends, чтобы появиться в ответе /me/friends

    let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
    fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
    fbLoginManager.logIn(withReadPermissions: ["email","user_friends","public_profile"], from: self) { (result, error) in
        if (error == nil){

            let fbloginresult : FBSDKLoginManagerLoginResult = result!
            if fbloginresult.grantedPermissions != nil {
                if(fbloginresult.grantedPermissions.contains("email")) {
                    // Do the stuff
                }else {

                }
            }else {
            }
        }
    }

Итак, во время входа в FB, это окно подсказки, которое содержит все разрешения ввести описание изображения здесь

Если кнопка нажать кнопку продолжения, будут установлены разрешения. Когда вы получаете доступ к списку друзей, используя FBGraphAPI, ваши друзья, которые вошли в приложение, как указано выше, будут перечислены

   if((FBSDKAccessToken.current()) != nil){
        FBSDKGraphRequest(graphPath: "/me/friends", parameters: ["fields" : "id,name"]).start(completionHandler: { (connection, result, error) -> Void in
            if (error == nil){

                print(result!)
            }
        })
    }

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

{
    data =     (
                {
            id = xxxxxxxxxx;
            name = "xxxxxxxx";
        }
    );
    paging =     {
        cursors =         {
            after = xxxxxx;
            before = xxxxxxx;
        };
    };
    summary =     {
        "total_count" = 8;
    };
}

Ответ 8

Я не знаю, изменили ли они это или нет. Но так я всегда получаю все данные моего друга. Даже те, которые не используют приложение.

//Configure the request
NSString *fbToken = [FBSession activeSession].accessTokenData.accessToken;
NSString *fbURL = @"https://graph.facebook.com/me/friends?access_token=%@";
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",fbURL, fbToken]]; 
NSURLRequest *friendRequest = [NSURLRequest requestWithURL:URL];

//Send the request
[NSURLConnection sendAsynchronousRequest:friendRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

    if (!connectionError) {

        NSDictionary *friendData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];

        NSLog(@"friend data = %@",friendData);

        //Grab friends ID (or whatever else you want, just replace dic[@"id"]] with dic[@"name"]] or something

        NSMutableArray *friendIDArray = [NSMutableArray array];

        for (NSDictionary *dic in friendData[@"data"]) {

            [friendIDArray addObject:dic[@"id"]];
        }

    }

}