Pull to refresh

Начало работы с Facebook SDK для iOS

Reading time8 min
Views20K
Сегодня важным элементом приложения является возможность сообщить вашим друзьям, коллегам, а также всему миру, что вы делаете, где и почему. В этом нам помогают социальные сети. Сети в свою очередь стараются облегчить труд программиста и создают свои SDK. Сейчас мы рассмотрим Facebook SDK для платформы iOS.

Когда я начинал разбираться с Facebook SDK примерно год назад, еще была версия, которая называлась facebook-iphone-sdk. Она и сейчас работает. Но я стал замечать странные сбои в приложении и решил, проверить не вышло ли что-то нового. Оказалось этот проект законсервирован, а разработчикам рекомендуется теперь использовать facebook-ios-sdk, где как утверждалось все еще проще. И как оказалось, не соврали.


Скачать SDK можно отсюда — https://github.com/facebook/facebook-ios-sdk

Пользователи Xcode 4.0 могут напрямую скачать SDK из среды разработки. Для этого открываем Organizer, идем в раздел Repositories, и после плюсика в нижнем левом углу выбираем Checkout or Clone Repository.

image

В директории Sample находится демо-приложение, которое мы и будем немного дорабатывать и по пути разбираться с ним. Но с ним сразу же возникла проблема при его попытке запустить. Какие-то параметры проекта мешают его компиляции в новом Xcode4 и выдается ошибка о неверной архитектуре. Надо зайти в параметры проекта и просто поубивать все в разделе Архитектура и затем у проекта восстановить только два параметра:
Architectures — Standard (arms6, arms7)
BaseSDK — Latest iOS (iOS 4.3)

image

Если не получилось, то ниже я прилагаю рабочий проект.

После этого наш проект начинает строится, запускается в симуляторе и сразу же «вылетает», сигнализирую в консоль — «2011-03-29 11:49:02.301 DemoApp[3098:207] missing app id!». Все потому, что мы не указали id нашего приложения.

Идем на http://www.facebook.com/developers/ и либо выбираем ваше уже существующие приложение или нажимаем кнопочку «+ Создать новое приложение». После несложной регистрации система сообщит вам кроме всего прочего «ID приложения». Он и только он нам и нужен в новой версии SDK. В файле DemoAppViewController.m мы можем увидеть такой текст:

// Your Facebook APP Id must be set before running this example
// See www.facebook.com/developers/createapp.php
// Also, your application must bind to the fb[app_id]:// URL
// scheme (substitue [app_id] for your real Facebook app id).
static NSString* kAppId = nil;


Заменяем nil на наш id приложения. Не забываем, что это строка. Пусть ДЛЯ ПРИМЕРА наш id будет 11111111111

static NSString* kAppId = @"11111111111";

Идем в конфигурационный файл Resources/DemoApp-Info.plist и заменяем значение fb[your-app-id] на картинке на наше fb11111111111 — причем скобки вас не должны смущать, ставить значение нужно без них!

image

Все! Теперь мы получили полностью работающее приложение. Можем запускать и изучать, что оно умеет. Сразу после запуска видим приглашение войти. После набора емейла и пароля появляется экран, интересующийся у нас, позволяем ли мы этому приложению получить доступ к нашим данным. Нажимаем Allow и попадаем в очень минималистический дизайн программы. На этом остановимся, чтобы разобраться как это все сейчас работает.

image

image

image

Для начала взглянем на XIB файл.

image

Он предельно прост — 5 кнопочек. При запуске приложения показывается только FBLoginButton — она у нас управляется одноименным классом, который добавлен в проект. Взглянем на метод viewDidLoad

/**
* Set initial view
*/
(void)viewDidLoad {
// инициализируем facebook
_facebook = [[Facebook alloc] initWithAppId:kAppId];
[self.label setText:@"Please log in"];
// скрываем все кнопочки крочем логина
_getUserInfoButton.hidden = YES;
_getPublicInfoButton.hidden = YES;
_publishButton.hidden = YES;
_uploadPhotoButton.hidden = YES;
_fbButton.isLoggedIn = NO;
[_fbButton updateImage];
}


Заметьте, как просто теперь инициализируется объект класса Facebook — нужно id приложения и все.

_facebook = [[Facebook alloc] initWithAppId:kAppId];
 
при нажатии кнопочки вызывается метод:

/**
* Called on a login/logout button click.
*/
- (IBAction)fbButtonClick:(id)sender {
if (_fbButton.isLoggedIn) {
[self logout];
} else {
[self login];
}


И если мы еще не вошли, то вызываем метод: 

/**
* Show the authorization dialog.
*/
- (void)login {
[_facebook authorize:_permissions delegate:self];
}


, где _permissions — это настройки прав на различные действия в facebook. В нашем примере мы просим права при инициализации нашего контроллера

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

...
_permissions = [[NSArray arrayWithObjects:
@"read_stream", @"offline_access",nil] retain];
}

...
}


согласно документации тут — http://developers.facebook.com/docs/authentication/permissions/, read_stream — это право читать ленту, а offline_access — это возможность вашему приложения оставаться присоединенным гораздо дольше. Без этого права токены на доступ сгорят достаточно быстро. Мы вернемся к этой теме чуть позже. Кстати, именно подтверждение этих прав у нас и запрашивалось, когда мы нажали кнопку Allow выше.

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

Еще один очень важный момент связан с тем, как же мы возвращаемся в наше приложение после авторизации. Помните, мы вносили какие-то параметры выше в файл Resources/DemoApp-Info.plist. И если мы присмотримся к файлу DemoAppAppDelegate.m то увидим там маленький метод, который и ответственен за возврат в наше приложение.

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [[controller facebook] handleOpenURL:url];
}


Таким образом, подытожим. Если вы будете делать приложение с нуля, то для успешной работы SDK вам обязательно надо

1. Указать app ID в коде
2. Указать app ID в файле параметров вашего приложения
3. В *AppDelegate.m вашего приложения реализовать метод

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [[controller facebook] handleOpenURL:url];
}


Без последних двух пунктов вы не будете возвращаться в ваше приложение после окна авторизации.

В зависимости от того с чем вы вернулись в приложение, вызывается один из трех методов, которые мы прописали, как делегат.
/**
* Вызывается, если мы успешно авторизовались.
*/
- (void)fbDidLogin {
[self.label setText:@"logged in"];
_getUserInfoButton.hidden = NO;
_getPublicInfoButton.hidden = NO;
_publishButton.hidden = NO;
_uploadPhotoButton.hidden = NO;
_fbButton.isLoggedIn = YES;
[_fbButton updateImage];
}

/**
* Вызывается, если мы отказались от авторизации.
*/
-(void)fbDidNotLogin:(BOOL)cancelled {
NSLog(@"did not login");
}

/**
* Вызывается, если мы сделали logout на фейсбуке.
*/
- (void)fbDidLogout {
[self.label setText:@"Please log in"];
_getUserInfoButton.hidden = YES;
_getPublicInfoButton.hidden = YES;
_publishButton.hidden = YES;
_uploadPhotoButton.hidden = YES;
_fbButton.isLoggedIn = NO;
[_fbButton updateImage];
}


Так как мы успешно авторизовались, все наши остальные кнопки появились на экране. Можете сами изучить, что они делают; мы подробнее вернемся к ним позже. Например, кнопка «Call Graph API» покажет вам ваше имя и фамилию, а кнопка «Call REST API» покажется вам имя и фамилию человека в id=4, а именно Mike Zuckerberg. Попытки узнать, кто же скрывается под id=1 не увенчались успехом и приложение крешнулось. Так что, желающие могут изучить этот вопрос самостоятельно. И вот пока я писал последний абзац мне пришлось как минимум несколько раз снова жать кнопку Login. Что же можно сделать, чтобы один раз авторизовавшись в приложении, не надо было входить на facebook снова. Сейчас этим и займемся. Все что нам для этого надо — это сохранить в NSUserDefaults значения двух переменных. viewDidLoad у нас примет вот такой вид:

- (void)viewDidLoad {
_facebook = [[Facebook alloc] initWithAppId:kAppId];

_facebook.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:@"AccessToken"];
_facebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:@"ExpirationDate"];

if ([_facebook isSessionValid] == NO) {
[self.label setText:@"Please log in"];
_getUserInfoButton.hidden = YES;
_getPublicInfoButton.hidden = YES;
_publishButton.hidden = YES;
_uploadPhotoButton.hidden = YES;
_fbButton.isLoggedIn = NO;
[_fbButton updateImage];
} else {
[self.label setText:@"logged in"];
_getUserInfoButton.hidden = NO;
_getPublicInfoButton.hidden = NO;
_publishButton.hidden = NO;
_uploadPhotoButton.hidden = NO;
_fbButton.isLoggedIn = YES;
[_fbButton updateImage];
}
}


мы проверяем валидна ли (isSessionValid) сессия с параметрами accessToken и expirationDate, сохраненных в нашем NSUserDefaults. Если да, то мы уже залогинены и можем выполнять дальнейшие действия.

в методы fbDidLogin и fbDidLogout мы добавим соответственно

fbDidLogin — записываем параметры

[[NSUserDefaults standardUserDefaults] setObject:self.facebook.accessToken forKey:@"AccessToken"];
[[NSUserDefaults standardUserDefaults] setObject:self.facebook.expirationDate forKey:@"ExpirationDate"];


fbDidLogout — обнуляем их

[[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"AccessToken"];
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"ExpirationDate"];


Запускаем нашем приложение, авторизуемся, проверяем нашем имя — все отлично. Не нажимая Logout прекращаем наше приложение и снова его запускаем — видим, что мы все еще на фейсбуке — проверяем наше имя — все работает. Напомню, что именно для этого мы в правах просили себе право @«offline_access». Этот вариант проекта можно будет скачать ниже.

И в заключение сегодняшней статьи давайте посмотрим как работает механизм запросов и обработка ответов.

Facebook настойчиво просит нас использовать GRAPH API, а не использовать старые REST API, который тем не менее все еще поддерживается. На этой странице (http://developers.facebook.com/docs/reference/api/) мы можем видеть примеры использования GRAPH API и посмотреть вывод информации для вас. В демо-программе facebook использует такой запрос:

- (IBAction)getUserInfo:(id)sender {
[_facebook requestWithGraphPath:@"me" andDelegate:self];
}


для примера они показывают какой вывод будет при таком запросе
graph.facebook.com/btaylor

{
«id»: «220439»,
«name»: «Bret Taylor»,
«first_name»: «Bret»,
«last_name»: «Taylor»,
«link»: «www.facebook.com/btaylor»,
«username»: «btaylor»,
«gender»: «male»,
«locale»: «en_US»
}

Это означает, что мы получим в ответ NSDictionary и сможем получить наше полное имя по ключу «name». После того, как мы отправили запрос и назначили получателя ответа, нам надо сделать методы, отвечающие за прием ответа. За прием неразобранного (raw) ответа отвечает

- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
NSLog(@"received response");
}


а разобранный ответ принимает:

- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
if ([result objectForKey:@"owner"]) {
[self.label setText:@"Photo upload Success"];
} else {
[self.label setText:[result objectForKey:@"name"]];
}
};


Ответ (result) может быть числом, строкой, массивом или dictionary. Если нам пришел ответ сразу как NSArray — значит нам пришел сразу несколько ответов и надо разбивать их по отдельным ответам. В данном случае когда мы попросили рассказать, кто же мы (me) такие, приходит ответ в NSDictionary и там нет ключа owner, и следовательно вызывается эта строчка:
[self.label setText:[result objectForKey:@"name"]];

Давайте все усложним. Например, я хочу получить в приложении информацию о том, что же любит пользователь. Изменим запрос согласно документации на:

- (IBAction)getUserInfo:(id)sender {
[_facebook requestWithGraphPath:@"me/likes" andDelegate:self];
}


если вы посмотрите на сайте фейсбука вывод этого запроса, то увидите, что ответом будем — NSDictionary всего с одним ключом data. Ему будет соответствовать массив объектов NSDictionary c ключами «name»,«category»,«id»,«created_time». Важно также не забыть в запрашиваемые права добавить «user_likes», иначе в ответ нам вернется пустой объект NSDictionary!

Чтобы принять такой ответ, перепишем наш метод

- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
if ([result objectForKey:@"owner"]) {
[self.label setText:@"Photo upload Success"];
} else if ([result objectForKey:@"data"]){
NSArray *likes = [result objectForKey:@"data"];
NSString *text=@"You don't like Steve";
for (NSDictionary* mylike in likes) {
NSString *mylikeName = [mylike objectForKey:@"name"];
if ([mylikeName isEqualToString:@"Steve Jobs"]) {
text=@"You like Steve";
break;
}
}
[self.label setText:text];
}
};


Если мы получили ответ в виде NSDictionary, и там есть значение по ключу «data», то мы проходим по этому массиву, заглядывая в каждый объект и проверяем, нет ли среди страничек facebook, которые нравятся пользователю, странички с именем Steve Jobs. У меня такая оказалась, так что приложение выдало мне «You like Steve».

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

Проект оргинальный, но с поправками в свойствах, чтобы запустился в Xcode4

Проект с добавленной возможностью сохранять сессию

Проект с измененным запросом

Только вам в них надо будет поставить свои appID :)

ps и еще я люблю форматирование кода на Хабре :)
Tags:
Hubs:
+24
Comments3

Articles

Change theme settings