- PVSM.RU - https://www.pvsm.ru -

Интеграция Facebook SDK (iOS) в мобильные free-to-play игры

imageМобильный free-to-play уже практически не обходится без использования социальных сетей в играх. Социальные сети обеспечивают то, что называют виральностью [1] (от англ. viral – вирусный, т.е. способный распространяться как вирус, от одного человека к другому), что позволяет привлекать новых игроков с минимальными затратами. В данной статье мы поделимся опытом интеграции Facebook в игры Alawar на платформе iOS.

В типичной мобильной free-to-play-игре интеграция Facebook предоставляет следующие возможности:

  1. Возможность авторизации через аккаунт пользователя в Facebook. Такая авторизация хороша тем, что мы формируем уникальный идентификатор пользователя игры с минимальными усилиями с его стороны. Игроку нет необходимости придумывать логин, вводить адрес электронной почты и т.д. Ведь речь идет о мобильном приложении, где постоянно вводить текстовые данные несколько утомительно. Достаточно однажды нажать на кнопку с хорошо узнаваемым логотипом “f”. Есть преимущества и для разработчиков – прогресс игрока может быть синхронизован между различными устройствами на разных платформах, главное чтобы на этих платформах был Facebook.
  2. Возможность отправлять запросы (app requests) друзьям. Запросы – это своего рода сообщения, которые появляются на Facebook-странице игрока (и в приложении Facebook) и содержат относящуюся к игре информацию. К типичным запросам можно отнести приглашения друзей в игру, просьбы вида «отправь/подари мне что-либо» или «помоги пройти уровень». В конечном счёте, запрос нужен только для того, чтобы человек нажал на него и перешел в игру (или на страницу игры в App store). Использование запросов – это мощный механизм для бесплатного привлечения новых игроков и возвращения старых игроков в игру.
  3. Возможность отправлять посты в таймлайн (timeline). Такой постинг позволяет сообщать об игре всем, кто читает таймлайн. Игры обычно помещают туда информацию о достижениях игрока, что добавляет соревновательную составляющую.
  4. Возможность получать информацию об игроке из его аккаунта в Facebook. Чаще всего в такую информацию входит имя игрока, его аватарка и, возможно, аватарки его друзей. Пример использования аватарок в реально существующей free-to-play-игре [2] под спойлером.
    Игра с аватарками

    image

Для того чтобы упростить интеграцию Facebook, мы создали демо-приложение, реализующее практически все необходимое с клиентской стороны.

Демо-приложение

Демо-приложение написано на C++ (как и большинство наших free-to-play-игр) со вставками на Objective-C и представляет собой простейшее приложение на движке Cocos2d-x [3] (скачать можно здесь [4]). По экрану перемещается прямоугольник и отражается от границ экрана при достижении этих границ.
image
Управление осуществляется при помощи 4 кнопок: f – авторизация, REQ – отправка запроса друзьям, POST – постинг в таймлайн и LOGOUT. После авторизации на место прямоугольника помещается аватарка пользователя, и добавляется приветственная надпись.
image
Для компиляции необходимо скачать и установить Facebook SDK [5], добавить фреймворк Facebook SDK в проект в XCode. Мы использовали Facebook SDK версии 3.9.
Для запуска демо-приложения вы должны создать собственное Facebook-приложение на вашем портале разработчика, прописать его идентификатор в plist нашего демо-приложения, а также проделать ряд других несложных операций. Подробнее про это можно прочитать здесь [6].

Технические детали реализации

Права доступа

Первое, с чем сталкиваешься при интеграции Facebook на клиенте – это создание сессии с запросом прав доступа (permissions), разные варианты [7] которых предлагает Facebook. Здесь важно сдержать порыв и не запросить права доступа на все сразу. Хорошим тоном считается запрос прав доступа в момент, когда они действительно необходимы. При создании сессии запросим только базовые права.

+ (NSArray*) getBasicPermissions
{
    NSArray* permissions = [[NSArray alloc] initWithObjects: @"user_birthday", nil];
    return permissions;
}

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

image

В противном случае iOS попытается использовать приложение Facebook (если оно было установлено из App Store) или откроет Facebook в браузере.
Когда нам потребуются права для опубликования чего-либо в таймлайн или при запросе друзьям, запросим права конкретно на это.

+ (void) requestPublishPermissions
{
    if (hasPublishPermissions) return;
    
    NSArray *permissions = [[NSArray alloc] initWithObjects:
                            @"publish_actions", @"publish_stream", nil];
    
    [[FBSession activeSession] requestNewPublishPermissions:permissions
                               defaultAudience:FBSessionDefaultAudienceFriends
                               completionHandler:^(FBSession *session, NSError *error)
    {
        hasPublishPermissions = [[FBSession activeSession].permissions containsObject:@"publish_actions"] &&
                                [[FBSession activeSession].permissions containsObject:@"publish_stream"];
        if (g_handler) { g_handler->OnGetPublishPermissions(hasPublishPermissions); }
    }];
}

В этом случае будет показан следующий диалог.
image
Важно помнить, что игрок может отказать в правах на опубликование от своего имени, но разрешить авторизацию. Эти случаи необходимо аккуратно обрабатывать.

Запросы к друзьям

Реализация запросов к друзьям (app requests) достаточно подробно изложена в документации [8], однако есть ряд тонкостей. При базовой реализации запросов будет показано диалоговое окно, приведенное ниже.
image
Код этой реализации находится под спойлером.

Реализация

+ (void) requestFriend
{
    // more details here
    // https://developers.facebook.com/docs/ios/send-requests-using-ios-sdk/
    
    if (!friendsCache)
    {
        friendsCache = [[FBFrictionlessRecipientCache alloc] init];
    }
    
    [friendsCache prefetchAndCacheForSession:nil];
    
    [FBWebDialogs presentRequestsDialogModallyWithSession:nil
                  message:@"Help me, friend!"
                  title:@"Help me!"
                  parameters:nil
                  handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error)
    {
        if (error)
        {
            NSLog(@"Error sending request");
        }
        else
        {
            if (result == FBWebDialogResultDialogNotCompleted)
            {
                NSLog(@"User canceled request");
            }
            else
            {
                NSDictionary *urlParams = [FacebookController parseURLParams:[resultURL query]];
                if (![urlParams valueForKey:@"request"])
                {
                    NSLog(@"User canceled request");
                }
                else
                {
                    NSLog([NSString stringWithFormat: @"Request Sent: %@", [urlParams valueForKey:@"request"]]);
                }
            }
        }
    }
    friendCache:friendsCache];
}

Поведением окна запросов можно управлять при помощи параметров, передаваемых после parameters: при вызове диалога. Например, следующий набор параметров позволяет отправить запрос одному известному заранее другу.

NSString *friendId=@"100006530868327";
NSMutableDictionary* params = [[NSMutableDictionary alloc] init];
params[@"to"] = friendId;

Facebook SDK в этом случае покажет видоизменённое окно App requests и даже предложит не показывать окно при следующих запросах этому другу.

Постинг в таймлайн

Facebook позволяет размещать в таймлайн так называемые истории (stories). Для того чтобы сформировать историю необходимо на странице вашего приложения на портале Facebook-разработчика [9] выбрать в меню слева «Open Graph». Вы увидите 3 вкладки: Stories (Истории), Object Types (Типы объектов) и Action Types (Типы действий). Тип объекта обобщает некоторую группу объектов, которые могут встретиться пользователям вашей игры, например, бейджи. Теперь нужны действия, которые можно применить к объекту «Бейдж», например, действие «Найти». Тип действия и тип объекта могут быть объединены в историю «Find a Badge». Facebook имеет широкие возможности по настройке историй. Подробнее об этом можно узнать здесь [10].
Чтобы получить код для публикации созданной истории из приложения, необходимо кликнуть на надпись «Get Code» справа от названия истории. Выбираем требуемую платформу (iOS SDK в нашем случае) и вкладку «Code for Object». Система сгенерирует код подобный этому:

NSMutableDictionary<FBGraphObject> *object =
    [FBGraphObject openGraphObjectForPostWithType:@"aw_test:badge"
                                            title:@"Sample Badge"
                                            image:@"https://fbstatic-a.akamaihd.net/images/devsite/attachment_blank.png"
                                              url:@"http://samples.ogp.me/473380876115865"
                                      description:@""];;

[FBRequestConnection startForPostWithGraphPath:@"me/objects/aw_test:badge"
                                   graphObject:object
                             completionHandler:^(FBRequestConnection *connection,
                                                 id result,
                                                 NSError *error) {
                                 // handle the result
                             }];

Во вкладке «Code for Action» можно получить код для действия. Для формирования истории в таймлайн сначала необходимо выполнить создание объекта затем создание действия с этим объектом.
Как ни странно, этот код не работает. Если включить его в проект без модификаций, то будет крэш по необнаруженному селектору в одном из классов. Для того чтобы это исправить, необходимо добавить пару строк в параметры создаваемого объекта Open Graph.

object[@"create_object"] = @"true";
object[@"fbsdk:create_object"] = @"true";

Эта «магия» исправляет крэш, однако постинг в таймлайн по-прежнему не работает. Хорошим признаком того, что все заработало, является возвращенный Facebook идентификатор операции в коллбэке. Идентификатор представляет собой набор цифр (например, 586146891470767). В ответ на код, полученный с портала, приходит строка «true». Это можно исправить, еще немного модифицировав код. В частности, предлагаемый кодогенератором вызов startForPostWithGraphPath необходимо заменить на startForPostOpenGraphObject при создании объекта.
Полный код для постинга в таймлайн можно увидеть под спойлером.

Реализация

+ (void) createOpenGraphObjectWithType:(NSString *) type
                                 title:(NSString *) title
                                   url:(NSString *) url
                                 image:(NSString *) image
                                 handler: (OpenGraphObjectCreationHandler) handler
{
    NSMutableDictionary<FBOpenGraphObject> *object =
    [FBGraphObject openGraphObjectForPostWithType:type
                                            title:title
                                            image:image
                                              url:url
                                      description:@""];
    object[@"create_object"] = @"true";
    object[@"fbsdk:create_object"] = @"true";
    
    [FBRequestConnection startForPostOpenGraphObject:object
                                   completionHandler:^(FBRequestConnection *connection, id result, NSError *error)
     {
         if (!error && result != nil)
         {
             NSLog([NSString stringWithFormat:@"Posting object '%@' (id=%@) is created!",
                    title, [result objectForKey:@"id"]]);
             
             handler([result objectForKey:@"id"]);
         }
         else
         {
             NSLog([NSString stringWithFormat:@"Posting object creation error: %@", error]);
         }
     }];
}

+ (void) postStory
{
    NSString* badge_title = @"Blue Badge";
    NSString* badge_url = @"http://demo.tom3.html5.services.alawar.com/images/tester/blue_badge.htm";
    NSString* badge_image = @"http://demo.tom3.html5.services.alawar.com/images/tester/blue_badge.png";
    int rnd = arc4random() % 2;
    if (rnd == 0)
    {
        badge_title = @"Red Badge";
        badge_url = @"http://demo.tom3.html5.services.alawar.com/images/tester/red_badge.htm";
        badge_image = @"http://demo.tom3.html5.services.alawar.com/images/tester/red_badge.png";
    }
    
    [FacebookController createOpenGraphObjectWithType:@"aw_test:badge"
                                                title:badge_title
                                                url:badge_url
                                                image:badge_image
                                                handler:^(NSString *objectId)
    {
        // action
        NSMutableDictionary<FBGraphObject> *action = [FBGraphObject graphObject];
        action[@"badge"] = objectId;
        action[@"fb:explicitly_shared"] = @"1";
        
        [FBRequestConnection startForPostWithGraphPath:@"me/aw_test:find"
                                           graphObject:action
                                     completionHandler:^(FBRequestConnection *connection,
                                                         id result,
                                                         NSError *error)
         {
             if (!error && result != nil)
             {
                 NSLog([NSString stringWithFormat:@"Posted (id=%@)!", [result objectForKey:@"id"]]);
             }
             else
             {
                 NSLog([NSString stringWithFormat:@"Posting error: %@", error]);
             }
         }];
    }];
}

Внимательный читатель обратит внимание, что при выполнении кода для действия был добавлен дополнительный параметр.

action[@"fb:explicitly_shared"] = @"1";

Если этот параметр не выставлен, то созданная история попадет в Recent Activities пользователя, а не в таймлайн. Если все сделано правильно, то в таймлайн пользователя можно будет наблюдать нечто, похожее на рисунок ниже.

image

Последний момент, с которым необходимо разобраться: где и как хранятся описания объектов. Описание объекта хранится как специальным образом размеченный html-документ на каком-либо открытом сервере. Изображения также размещаются на открытых серверах, откуда их Facebook и загружает.

Вместо заключения

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

Автор: rokuz

Источник [11]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ios/56105

Ссылки в тексте:

[1] виральностью: http://en.wikipedia.org/wiki/Viral_marketing

[2] free-to-play-игре: https://www.facebook.com/TheTreasuresOfMontezumaBlitzCommunity

[3] Cocos2d-x: http://www.cocos2d-x.org/

[4] здесь: https://github.com/alawar/FacebookSample

[5] Facebook SDK: https://developers.facebook.com/docs/ios/

[6] здесь: https://developers.facebook.com/docs/ios/getting-started/

[7] разные варианты: https://developers.facebook.com/docs/reference/login/#permissions

[8] документации: https://developers.facebook.com/docs/ios/send-requests-using-ios-sdk/

[9] портале Facebook-разработчика: https://developers.facebook.com/apps

[10] здесь: https://developers.facebook.com/docs/opengraph/creating-custom-stories/

[11] Источник: http://habrahabr.ru/post/214043/