- PVSM.RU - https://www.pvsm.ru -
Итак, я не буду рассказывать про то, что такое социальные сети и как они используются в игровых (да и неигровых) приложениях. Скажу просто, что однажды поставили передо мной задачу научить нашу игру публиковать всякие разные вещи в Facebook и Twiter.
Игра у нас создается с использованием движка Unity3d. Никаких встроенных возможностей по работе с социальными сетями в нем не предусмотрено. Зато есть возможность писать плагины на c/c++/objective c/… Т.е. на нативном языке платформы. Этим и предстояло мне заняться. Приложение мы разрабатываем под ipad, соответственно платформа iOS и язык Objective-C.
Ниже я расскажу, что и как у меня получилось, поделюсь кодом и задам пару вопросов уважаемому хабрасообществу.
Сразу уточню, что плагин еще не отлажен до конца! Но чем поделиться все равно найдется.
Существует несколько вариантов, как создать такой плагин:
WebView
и Client-side/Server-side
варианты отсюда [2]. Но мне кажется это не очень удобно для мобильных приложений, если только ваше приложение не на HTML5
Но есть и еще один вариант, о котором, как вы уже догадались, я и расскажу. Apple в 6ой версии iOS добавила более тесную интеграцию с Facebook. Настолько тесную, что в настройках девайсов появился одноименный пункт в котором можно было вбить свои логин/пароль от соц. сети. Такая же возможность есть и для Twitterа. При чем Twitter в настройках появился в более ранних версиях iOS.
Для работы с данными аккаунтами Apple предоставляет довольно удобные фреймворки Accounts
, Social
и Twitter
. Первый — Accounts
— предоставляет доступ к аккаунтам, заданным пользователем в настройках девайса/ Основная идея такая — указываем один раз логин/пароль от аккаунта в соц. сети и пользуемся им во всех приложениях, без необходимости каждый раз вводить их заново. Второй — Social
— помогает формировать запросы к соц. сетям в соответствии с их (сетей) требованиями. Ну а третий — Twitter
— не тема данной статьи, т.к. здесь я рассматриваю только работу с Facebook.
Отвечая на заданный в заголовке параграфа вопрос, скажу: лично я считаю, что велосипед изобретать не стоило и проще было купить, но у меня было много свободного времени и решено было попробовать самостоятельно сваять плагин. К тому же дополнительный опыт никому не мешал.
Все просто — кладем плагин в Assets/Plugins/iOS
и он автоматически подцепляется движком при следующей генерации xcode-проекта. В качестве плагина может выступать:
При использовании C++ или Objective-C кода экспортируемые функции должны быть объявлены в С стиле, дабы избежать проблем с различиями в способах вызова функций.
И еще одно замечание — функции из плагинов могут быть вызваны только на конкретных девайсах, поэтому, чтобы избежать проблем при запуске проекта в симуляторах, рекомендуется все импортируемые (в проект из плагина) функции дополнительно заворачивать в С# код, проверяющий на какой платформе запускается проект.
Здесь все.
Необходимо правильно настроить приложение в Facebook прежде, чем начинать с ним работу. Если этого не сделать — можно поймать кучу «мистических» и не очень багов.
Вот мои рекомендации по настройке:
Native iOS App
. Делает это на двух страницах: Настройки — Основные, там где спрашивают как ваше приложение встроено в Facebook, и Настройки — Advanced. Здесь нас интересует раздел Authentification
, параметр App type
-> Native/Desktop
.Bundle ID
. Естественно он должен совпадать с тем, который указан в настройках проекта.App Secret in Client
ставим в НетFacebook Login
— включен
Собственно, алгоритм довольно прост — получаем у системы аккаунт, запрашиваем права на публикацию данных в соц. сеть, публикуем. А теперь о каждом из пунктов поподробнее.
Как я писал выше, в iOS SDK есть фреймворк Accounts
, который предоставляет доступ к аккаунтам пользователя, заданным в настройках. На самом деле не только к ним, но другие нас не интересуют.
Для начала необходимо создать объект класса ACAccountStore
, который представляет собой хранилище аккаунтов. Затем получаем объект класса ACAccountType
, который будет содержать информацию обо всех аккаунтах, интересующего нас типа.
Поддерживаются три типа аккаунтов:
ACAccountTypeIdentifierFacebook
— аккаунты Facebook ACAccountTypeIdentifierTwitter
— аккаунты Twitter ACAccountTypeIdentifierSinaWeibo
— а это, неинтересующая нас, китайская соц. сеть
Далее мы должны запросить доступ к аккаунтам данного типа с интересующими нас разрешениями. Разрешения специфичны для каждой сети и задаются в виде массива строк.
После этого мы спокойно можем получать аккаунт (при условии, конечно, что такие аккаунты существуют и пользователь разрешил все наши хотелки) и творить с ним все, что захотим.
Казалось бы все просто и примитивно. Но нет, не в случае с Facebook.
Алгоритм немного усложняется. Для того, чтобы iOS SDK совместно с Facebook нас не послала совсем и, чтобы нам не пришлось каждый раз показывать пользователю алерты с просьбами разрешить нам что-то, необходимо выполнить особый алгоритм запроса разрешений:
Подробнее про все возможные права/разрешения здесь [3].
Если не выполнить данный алгоритм — то можно словить вот такие ошибки:
Кому интересно можете почитать Stackoverflow на эту тему. Там народ много таких ошибок выкладывает!
Итак, у меня получился примерно такой код (восстанавливаю по памяти, сейчас он уже поменялся, а репозитория с историей под рукой нет):
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : @[@"email"], ACFacebookAudienceKey : ACFacebookAudienceEveryone};
[store requestAccessToAccountsWithType:fb_account_type
options:dict
completion:^(BOOL granted, NSError *error)
{
if (granted && error == nil)
{
ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : @[@"read_permission"], ACFacebookAudienceKey : ACFacebookAudienceEveryone};
[store requestAccessToAccountsWithType:fb_account_type
options:dict
completion:^(BOOL granted, NSError *error)
{
if (granted && error == nil)
{
ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : @[@"public_permission"], ACFacebookAudienceKey : ACFacebookAudienceEveryone};
[store requestAccessToAccountsWithType:fb_account_type
options:dict
completion:^(BOOL granted, NSError *error)
{
if (granted && error == nil)
{
// Тут можем уже получать аккаунт и постить в фейсбук
}
}];
}
}];
}
}];
Согласен, выглядит жутко!
Сразу пара замечай про постоянно пересоздаваемые dict
и fb_account_type
:
dict
. Если присмотреться, то там меняется только ACFacebookPermissionsKey. И в теории можно было бы воспользоваться [dict setObject: forKey:]
. НО, еще на стадии написания кода Xcode стал выдавать варнинги о том, что этот метод может и не заработать. В итоге все сбилдилось, запустилось на девайсе и… все-таки упало, именно на этом методе. Краш-дампы, каюсь, не читал, а просто обошел проблему вот таким образом, поставив себе TODO на будущее.fb_account_type
. Тут все немного сложнее. Без пересоздания объекта, у меня приложение стабильно крашилось на втором [store requestAccessToAccountsWithType:options:completion]
. Очередное TODO. Я этот код немного упростил:
void _fb_request_access(NSArray *permissions, FBAccessGrantedHandler handler)
{
ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : permissions, ACFacebookAudienceKey : ACFacebookAudienceEveryone};
[store requestAccessToAccountsWithType:fb_account_type
options:dict
completion:^(BOOL granted, NSError *error)
{
if (granted && error == nil)
{
handler();
}
}];
}
_fb_request_access(@[@"email"], ^()
{
_fb_request_access(@[@"read_stream"], ^()
{
_fb_request_access(@[@"publish_stream"], ^()
{
_fb_post_impl(post);
});
});
});
К сожалению, данный код протестить не успел — ipad забрали (а с 6ой версией он один у нас). Но проект собрался без ошибок.
Теперь о, собственно, постинге на стену. У меня этим занимается отдельная функция — _fb_post_impl
.
void _fb_post_impl(NSString *text)
{
ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSArray *accounts = [store accountsWithAccountType:fb_account_type];
fb_account = [accounts objectAtIndex:0];
SLRequest *fb_request = [SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodPOST
URL:[NSURL URLWithString:@"https://graph.facebook.com/me/feed"]
parameters:[NSDictionary dictionaryWithObject:text forKey:@"message"]];
[fb_request setAccount:fb_account];
[fb_request performRequestWithHandler:^(NSData* responseData, NSHTTPURLResponse* urlResponse, NSError* error)
{
NSLog([[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding]);
}];
}
Алгоритм прост:
ACAccountType
. Подразумевается, что все необходимые разрешения получены ранее.У меня сейчас постится только текст. Для того, чтобы добавить картинку/ссылку/заголовок/т.д. необходимо дополнить словарь parameters. Список возможных полей смотреть здесь [4]. Еще советую почитать вот это [5], если вам нужен не только постинг на стену.
Тут на самом деле все просто и примитивно.
[DllImport ("__Internal")]
private static extern void _fb_init(String appid);
[DllImport ("__Internal")]
private static extern void _fb_post(String text);
public static void fb_init() {
_fb_init("тут application id");
}
public static void fb_post(String text) {
_fb_post(text);
}
Маленькое замечание — типу String
из С#, соответствует const char*
в плагине. Я, например, пытался сначала использовать NSString
.
В итоге запостить текст (и не только) к себе на стену у меня получилось. Времени, правда, потратил немерено из-за вот тех мелких косяков, про которые я писал — использование плагина только на девайсе, хитрый алгоритм получения разрешений.
Код выложил на bitbucket — bitbucket.org/mnazarov/fb_plugin [6].
Кстати, с Objective-C столкнулся в первый раз, так что прошу сильно не пинать, а отнестись с пониманием. Рад буду получить советы как улучшить код.
Также рад буду если кто-то сможет попробовать у себя на девайсах и отпишется как работает.
Ну и очень рад буду если кто-то решит помочь с разработкой!
Ну и теперь пара вопросов:
[dict:setObject:forKey]
. При том, что Xcode об этом методе знал, да и на том же Stackoverflow народ его использует в таком же случае как у меня.P.S. Надесь данной статьей сэкономил кому-нибудь пару нервных клеток.
Автор: Greezlee
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/plugins/17580
Ссылки в тексте:
[1] Facebook SDK for iOS: http://developers.facebook.com/ios/
[2] отсюда: https://developers.facebook.com/docs/concepts/login/login-architecture/
[3] здесь: http://developers.facebook.com/docs/reference/login/#permissions
[4] здесь: https://developers.facebook.com/docs/reference/api/post/
[5] это: https://developers.facebook.com/docs/reference/api/
[6] bitbucket.org/mnazarov/fb_plugin: https://bitbucket.org/mnazarov/fb_plugin
Нажмите здесь для печати.