ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

29. Социальные сети

Вконтакте

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

 

Для доступа к API ВКонтакте из любого Standalone-приложения предусмотрен механизм клиентской авторизации на базе протоколаOAuth 2.0. В качестве клиента может выступать любое Desktop/мобильное приложение, имеющее доступ к управлению Web-браузером, в нашем случае это компонент UIWebView при создании приложения для iOS.

Процесс авторизации приложения состоит из 3-х шагов:

 

  1. Открытие окна браузера для аутентификации пользователя на сайте ВКонтакте.
  2. Разрешение пользователем доступа к своим данным.
  3. Передача в приложение ключа access_token для доступа к API.

 

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

 

Прежде, чем открывать Xcode, вам следует создать приложение вконтакте, чтобы у вас был его ID. Для этого перейдите по ссылке и выберите Standalone-приложение, затем нажмите подключить приложение. После создания приложения найти id созданного приложения не составит труда.

 

Теперь давайте создадим скелет нашего приложения и объявим в нём все данные и методы, которые нам в дальнейшем пригодяться. Наполнение мышечной массы в виде кода мы оставим на потом.

 

Для начала создадим проект на основании шаблона View-based Application, я назвал его iPhotoVKontakte. Далее в приложение следует добавить два очень полезных класса: APIDownload и Touch JSON. С первым классом вы наверняка уже встречались, напомню, что он служит для упрощёной закачки данных с интернета. Второй класс за вас распарсит полученные данные с ресурса ВКонтакте. Скачайте оба класса и добавьте в проект. А также нам прийдется добавить в проект UINavigationController, как это показано в этомпримере. А в iPhotoVKontakte.xib добавьте  TableView и WebView.

 

Теперь перейдём к интерфейсу класса iPhotoVKontakteViewController и его реализации:

 

<code data-result="[object Object]">#import &lt;UIKit/UIKit.h&gt;
#import "CJSONDeserializer.h"
#import "APIDownload.h"

@interface iPhotoVKontakteViewController : UIViewController
{
    UIWebView *web;
    NSMutableArray *allPhoto;
    UITableView *photoList;
    NSInteger offset;
    NSString *access_token;
}

@property (nonatomic, retain) IBOutlet UIWebView *web;
@property (nonatomic, retain) IBOutlet UITableView *photoList;
@property (nonatomic, retain) NSMutableArray *allPhoto;
@property (nonatomic, retain) NSString *access_token;

- (NSString*)stringBetweenString:(NSString*)start 
                       andString:(NSString*)end 
                     innerString:(NSString*)str;

@end</code>

 

Импортируем нужные нам классы, инициализируем переменные: web и photoList – браузер и таблица соответственно, в переменнойallPhoto будет храниться скачанная информация, а переменные offset и access_token понадобятся для авторизации и скачивания списка фотографий. Кроме этого, объявим функцию, которая будет находить подстроку (значение access_token) с того, что нам возвратит сервер ВКонтакте. Вроде всё. Перейдём к реализации скелета:

 

<code data-result="[object Object]">#import "iPhotoVKontakteViewController.h"

#define photoLink @"https://api.vk.com/method/photos.getAll?access_token=%@&amp;count=100&amp;offset=%i"
#define APIid @"Insert your APIid"

@implementation iPhotoVKontakteViewController

@synthesize web;
@synthesize photoList;
@synthesize allPhoto;
@synthesize access_token;

-(void) dealloc;
{
    self.web = nil;
    self.photoList = nil;
    self.allPhoto = nil;
    self.access_token = nil;
    [super dealloc];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.web = nil;
    self.photoList = nil;
}

- (NSString*)stringBetweenString:(NSString*)start 
                       andString:(NSString*)end 
                     innerString:(NSString*)str 
{
    NSScanner* scanner = [NSScanner scannerWithString:str];
    [scanner setCharactersToBeSkipped:nil];
    [scanner scanUpToString:start intoString:NULL];
    if ([scanner scanString:start intoString:NULL]) {
        NSString* result = nil;
        if ([scanner scanUpToString:end intoString:&amp;result]) {
            return result;
        }
    }
    return nil;
}</code>

 

Синтезируем, удаляем, освобождаем, впрочем всё как обычно. А так же тут реализация объявленной функции. И так, скелет готов! Теперь, как и обещал, давайте займёмся наращиванием мышечной массы. Сразу после запуска приложения стартует методviewDidLoad.

 

<code data-result="[object Object]">-(void)viewDidLoad
{
    [super viewDidLoad];

    offset = 0;
    self.access_token = [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];
    if (access_token) {
        [web removeFromSuperview];
        self.web = nil;

        self.allPhoto = [NSMutableArray array];
        NSString *photoRequest = [NSString stringWithFormat:photoLink, access_token, offset];
        [APIDownload downloadWithURL:photoRequest delegate:self sel:@selector(didLoadPhotoList:)];
    } else {
        NSString *authorizationLink = [NSString stringWithFormat:@"http://api.vk.com/oauth/authorize?client_id=%@%@", 
                                       APIid,
                                       @"&amp;scope=4&amp;redirect_uri=http://api.vk.com/blank.html&amp;display=touch&amp;response_type=token"];
        NSURL *url = [NSURL URLWithString:authorizationLink];

        web.hidden = NO;
        [web loadRequest:[NSURLRequest requestWithURL:url]];  
    }   
}</code>

 

Здесь мы проверяем, не заходили ли мы в это приложение ранее, если нет, то отправляем запрос примерно на такой адрес: http://api.vk.com/oauth/authorize?client_id=2864192&scope=4&redirect_uri=http://api.vk.com/blank.html&display=touch&response_type=token

 

Обратите внимание, что параметры client_id у каждого будет свой, тот, который вы запомнили вначале. А также в параметре scopeпередаём права доступа для приложения. Подробнее о правах здесь и здесь. Нашему приложению необходима информация только о фотографиях, это означает, что битовая маска будет равна 4.

 

В случае успешной авторизации ваш браузер будет перенаправлен  опять же примерно на такой адрес: http://api.vkontakte.ru/blank.html#access_token=533bacf01e11f55b536a565b57531ad114461ae8736d6506a3&expires_in=86400

 

Отделяем часть этой строки, сохраняем access_token код в одноимённую переменную, он нам пригодится для отправки запросов. Делается это всё в методе webViewDidFinishLoad:

 

<code data-result="[object Object]">-(void)webViewDidFinishLoad:(UIWebView *)webView {
    self.access_token = [self stringBetweenString:@"access_token=" 
                                        andString:@"&amp;" 
                                      innerString:[[[webView request] URL] absoluteString]];
    if (access_token) {
        [[NSUserDefaults standardUserDefaults] setObject:access_token forKey:@"access_token"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        self.allPhoto = [NSMutableArray array];
        NSString *photoRequest = [NSString stringWithFormat:photoLink, access_token, offset];
        [APIDownload downloadWithURL:photoRequest delegate:self sel:@selector(didLoadPhotoList:)];
    }
}</code>

 

Переменной access_token присвоен токен. Сохраняем его также в настройки при помощи  NSUserDefaults, чтобы следующий раз у нас приложение не запрашивало логин и пароль. Инициализируем массив, и начинаем закачку при помощи класса APIDownload. Обратите внимание, что переменная photoLink объявлена в #define. После успешной закачки вызывается метод  didLoadPhotoList:

 

<code data-result="[object Object]">- (void)didLoadPhotoList:(APIDownload*)request {
    CJSONDeserializer *deserializer = [[CJSONDeserializer new] autorelease];
    NSDictionary *dict = [deserializer deserializeAsDictionary:request.downloadData error:nil];

    //NSLog(@"%@", dict);

    NSArray *response = [dict objectForKey:@"response"];
    NSArray *photos = [response subarrayWithRange:NSMakeRange(1, response.count-1)];

    for (int i=0; i&lt;photos.count; i++) {
        NSDictionary *photo = [photos objectAtIndex:i];
        NSString *smallImageURL = [photo objectForKey:@"src_small"];
        NSMutableDictionary *item = [NSMutableDictionary dictionaryWithObjectsAndKeys:smallImageURL, @"small_url", nil];
        [allPhoto addObject:item];

        APIDownload *photoResponse = [APIDownload downloadWithURL:smallImageURL delegate:self];
        photoResponse.tag = [allPhoto indexOfObject:item];
    }

    NSInteger maxCount = [[response objectAtIndex:0] intValue];
    if ([allPhoto count] != maxCount) {
        offset +=100;
        NSString *photoRequest = [NSString stringWithFormat:photoLink, access_token, offset];
        [APIDownload downloadWithURL:photoRequest delegate:self sel:@selector(didLoadPhotoList:)];
    }    

    [photoList reloadData];
}

- (void)APIDownload:(APIDownload*)request {
    UIImage *photo = [UIImage imageWithData:request.downloadData];
    NSMutableDictionary *item = [allPhoto objectAtIndex:request.tag];
    [item setObject:photo forKey:@"photo"];
    [photoList reloadData];
}</code>

 

В методе didLoadPhotoList мы должны распарсить полученные данные, которые приходят нам в формате JSON. Именно это и делаем в первых двух строка. Затем, в цикле заполняем массив allPhoto ссылками на фотографии (но перед этим ссылки ложим в словарь). И здесь же начинаем каждую фотограяию. Но дело в том, что API Vkontakte имеет ограничение на количество фотографий в одном запросе (только 100 штук). Однако есть переменная offset, в которой мы храним смещение, необходимое для выборки определенного подмножества фотографий. Поэтому, как только мы закачиваем первые 100 фотографий, мы увеличиваем offset ровно на 100 и отправляем запрос на получение следующей порции картинок. Скачаное изображение возвращается в метод APIDownload:. В этом методе (по оставленой ранее метке) мы получаем словарь со ссылкой на скачаную картинку и добавляем эту картинку в него.  И так пока не загрузим все имеющиеся фотографии. Ну и в конце каждого метода не забываем обновить таблицу.

 

<code data-result="[object Object]">- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return allPhoto.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 72.0f;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                       reuseIdentifier:CellIdentifier] autorelease];
    }

    NSDictionary *item = [allPhoto objectAtIndex:indexPath.row];
    cell.imageView.image = [item objectForKey:@"photo"];

    return cell;
}</code>

 

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

 

Исходный код можно скачать здесь.

 

P.S. log out из приложения не предусмотрен, но вы легко сможете его реализовать с помощью удаления сохраненного токена:

 

<code data-result="[object Object]">NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults removeObjectForKey:@"access_token"];
[userDefaults synchronize];
<!--nextpage--></code>

Facebook

Во время развития социальных сетей процесс интеграции своего приложения с такой сетью становится очень остро. У нас есть возможность дать пользователям нашей программы «похвастаться» достижениями игры или разместить другую интересную и полезную информацию с приложения. В этом примере я покажу как это делать с сетью Facebook.

Для начала нам понадобиться ID приложения. Чтобы его получить — следует иметь свою учетную запись. Если она имеется — перейдите в самый низ главной странички там вы увидите ссылку Разработчикам.

Вы попадаете на страничку для разработчиков. Сверху странички есть кнопка Мои приложения, жмите на нее.

Теперь вы попадаете в список своих приложений. Скорее всего он пуст так, что смело жмите кнопку Создать новое приложение.

На открывшейся страничке в поле заполните поля и нажмите кнопку Create App.

 

Последует проверка безопасности.

Если все сделано правильно — вы увидите примерно следующую страничку.

 

После сохранения изменений вы получите ID приложения.

 

Поскольку ключ этот приватный и он используется для контроля работы разработчиков — я его частично затер.
Теперь создадим сам проект на основе представления (View-based Application) и назовем его facebookApp. Первым делом, что нам нужно — это добавить две библиотеки для работы с сайтом facebook.com. Первая — это API Facebook, вы ее могли скачать с самого сайта фэйсбука или (я советую) отсюда. Вторая библиотека — это JSON она используется для обработки данных в формате JSON. Скачать ее можно отсюда. Распакуйте обра архива и добавьте в проект.

Подключим баблиотеку Facebook, для этого изменим интерфейс класса facebookAppViewController:

<code data-result="[object Object]">#import "FBConnect.h"

@interface facebookAppViewController : UIViewController &lt;FBRequestDelegate, FBDialogDelegate, FBSessionDelegate&gt; {
    Facebook *facebook;
}

@property (nonatomic, retain) Facebook *facebook;
@property (nonatomic, retain) IBOutlet UIButton *fbButton;

- (IBAction)loginToFacebook;
- (void)initFacebook;

@end</code>

 

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

Перейдем к реализации класса facebookAppViewController:

<code data-result="[object Object]">#import "facebookAppViewController.h"

@implementation facebookAppViewController

@synthesize facebook;

- (void)dealloc
{
    self.facebook = nil;
    [super dealloc];
}

#pragma mark - View lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)loginToFacebook {
    [self initFacebook];

    NSString *path = [[NSBundle mainBundle] pathForResource:@"FBpermissions" ofType:@"plist"];
    NSDictionary *settingsDic = [NSDictionary dictionaryWithContentsOfFile:path];
    NSArray *permissions = [settingsDic objectForKey:@"facebookPermissions"];
    ;  

}

- (void)initFacebook {
    if (!facebook) {
        self.facebook = [[Facebook alloc] initWithAppId:@"Set App ID"];

        NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        facebook.accessToken = [userDefaults objectForKey:@"AccessToken"];
        facebook.expirationDate = [userDefaults objectForKey:@"ExpirationDate"];

        ;
    }
}

@end</code>

 

С примера видно, что я синтезировал методы доступа для переменной facebook и организовал для нее очистку памяти. В методе loginToFacebook инициализируется переменная facebook (если она не была инициализирована до этого) на основании полученного ранее ключа. Далее происходит присваивание переменным accessToken и expirationDate значений сохраненных при первом залогинивании (образно говоря, таким способом программа помнит логин и пароль пользователя, то есть, при повторном запуске программы пользователю не нужно будет снова вводить логин и пароль).

В методе loginToFacebook мы вызываем метод инициализации потом с созданного ранее plist файла получаем массив разрешений. Это те разрешения с которыми соглашается пользователь при входе под своей учетной записью в нашем приложении. После чего вызываем метод авторизации.

Теперь добавьте кнопку к интерфейсу и свяжите событие нажатия на эту кнопку с методом loginToFacebook. Если все сделано правильно, то при нажатии на эту кнопку вы получите следующую картинку:

Все хорошо, но как узнать залогинин пользователь в нашем приложении или нет, а если да, то под каким акаунтом? Для этого изменим интерфейс класса facebookAppViewController:

<code data-result="[object Object]">#import "FBConnect.h"

@interface facebookAppViewController : UIViewController &lt;FBRequestDelegate, FBDialogDelegate, FBSessionDelegate&gt; {
    Facebook *facebook;
    UIButton *fbButton;
}

@property (nonatomic, retain) Facebook *facebook;
@property (nonatomic, retain) IBOutlet UIButton *fbButton;

- (IBAction)loginToFacebook;
- (void)initFacebook;

@end</code>

 

Я добавил переменную типа UIButton свяжите ее с кнопкой интерфейса, на нее мы и будем выводить информацию о состоянии учетной записи.

В реализации класса facebookAppViewController добавьте два метода:

<code data-result="[object Object]">- (void)fbDidLogin {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:self.facebook.accessToken forKey:@"AccessToken"];
    [userDefaults setObject:self.facebook.expirationDate forKey:@"ExpirationDate"];
    [userDefaults synchronize];

    ;
}

- (void)fbDidLogout {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults removeObjectForKey:@"AccessToken"];
    [userDefaults removeObjectForKey:@"ExpirationDate"];
    [userDefaults removeObjectForKey:@"userName"];
    [userDefaults synchronize];

    [fbButton setTitle:@"Login" forState:UIControlStateNormal];
}</code>

Это метода делегата поэтому в интерфейсе мы их не объявляли. Метод fbDidLogin вызывается в момент успешной авторизации, как раз в нем мы и сохраняем информацию о сессии, чтобы не запрашивать в следующий раз логин и пароль. После сохранения информации о сессии мы запрашиваем информацию о имени пользователя, который авторизировался. Когда Facebook возвратит нам это имя — вызовется метод request didLoad. В нем мы проверяем какого рода эта информацию (пока что этот метод возвращает только имя пользователя, но это не единственная информацию поэтому я добавил проверку на тип возвращаемой информации). Елси это имя пользователя — тогда сохраняем имя в NSUserDefaults и устанавливаем кнопке fbButton надпись со значением имени пользователя.
Кроме этого я изменил метод viewDidLoad. В нем я устанавливаю кнопке fbButton надпись с именем пользователя, которое мы сохраняли ранее.

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];

    NSString *userName = [[NSUserDefaults standardUserDefaults] objectForKey:@"userName"];
    if (userName) {
        [fbButton setTitle:userName forState:UIControlStateNormal];
    }
}</code>

 

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

Если есть возможность войти под своей учетной записью — должна быть возможность выйти. Давайте реализуем этот функционал. Для этого изменим код метода loginToFacebook:

<code data-result="[object Object]">- (IBAction)loginToFacebook {
    [self initFacebook];

    if () {
        ;
    } else {
        NSString *path = [[NSBundle mainBundle] pathForResource:@"FBpermissions" ofType:@"plist"];
        NSDictionary *settingsDic = [NSDictionary dictionaryWithContentsOfFile:path];
        NSArray *permissions = [settingsDic objectForKey:@"facebookPermissions"];
        ;  
    }
}</code>

 

Правда, теперь это метод не только логина, но и логаута… Перед тем как выполнить какое-то действие мы проверяем валидность сессии. Если сессия валинда — выполняем логаут, если нет — открываем окно авторизации. Если логаут выполнен успешно — вызывается метод делегата fbDidLogout:

<code data-result="[object Object]">- (void)fbDidLogout {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults removeObjectForKey:@"AccessToken"];
    [userDefaults removeObjectForKey:@"ExpirationDate"];
    [userDefaults removeObjectForKey:@"userName"];
    [userDefaults synchronize];

    [fbButton setTitle:@"Login" forState:UIControlStateNormal];
}</code>

 

В этом методе мы удаляем значения сессии и имя пользователя которые сохраняли в момент авторизации и меняем кнопке надпись.

Приступим к работе непосредственно с учетной записью. Я добавил в интерфейс класса facebookAppViewController четыре метода:

 

  • publishFBStream
  • publishImageFBStream
  • getFriends
  • uploadPhoto

Добавте в файл facebookAppViewController.xib четыре кнопки и свяжите нажатие на них с вызовом этих методов. Ниже я приведу реализацию этих методов:

<code data-result="[object Object]">- (IBAction)publishFBStream {
    [self initFacebook];

    if () {        
        SBJSON *jsonWriter = [[SBJSON new] autorelease];

        NSDictionary* actionLinks = [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
                                                               @"Always Running",
                                                               @"text",
                                                               @"http://itsti.me/",
                                                               @"href", nil], nil];

        NSString *actionLinksStr = [jsonWriter stringWithObject:actionLinks];
        NSDictionary* attachment = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"a long run", @"name",
                                    @"The Facebook Running app", @"caption",
                                    @"it is fun", @"description",
                                    @"http://itsti.me/", @"href", nil];
        NSString *attachmentStr = [jsonWriter stringWithObject:attachment];
        NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       @"Share on Facebook",  @"user_message_prompt",
                                       actionLinksStr, @"action_links",
                                       attachmentStr, @"attachment", 
                                       @"Это отличный сайт! 
 http://imaladec.com", @"message", nil];
        ; 

    } else {
        [self loginToFacebook]; 
    }
}</code>

 

Этот метод постит сообщения на доску. Строка @»Это отличный сайт! http://imaladec.com», @»message», nil]; отвечает за содержание сообщения.

Метод, который постит картинку на доску очень похож:

<code data-result="[object Object]">- (IBAction)publishImageFBStream {
    [self initFacebook];

    if () {    
        SBJSON *jsonWriter = [[SBJSON new] autorelease];

        NSDictionary* actionLinks = [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
                                                               @"www.imaladec.com",
                                                               @"text",
                                                               @"http://www.imaladec.com/",
                                                               @"href", nil], nil];

        NSString *actionLinksStr = [jsonWriter stringWithObject:actionLinks];

        NSDictionary* imageShare = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"image", @"type",
                                    @"http://mycrealife.ru/wp-content/uploads/2014/01/logo1.png", @"src",
                                    @"http://imaladec.com/", @"href",
                                    nil];

        NSDictionary* attachment = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @"", @"caption",
                                    [NSArray arrayWithObjects:imageShare, nil], @"media",
                                    nil];

        NSString *attachmentStr = [jsonWriter stringWithObject:attachment];

        NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       @"Share on Facebook",  @"user_message_prompt",
                                       actionLinksStr, @"action_links",
                                       attachmentStr, @"attachment",
                                       nil];

        ;

    } else {
        [self loginToFacebook]; 
    }    
}</code>

 

Вы можете комбинировать эти два метода для достижения оптимального результата. Важно помнить, что картинка должна быть где-то в сети, то есть, на доску вы не закачиваете изображение, а оставляете на него ссылку.

С помощью следующего метода вы можете получить список друзей и информацию о них:

<code data-result="[object Object]">- (IBAction)getFriends {
    [self initFacebook];
    if () {
        ;        
    } else {
        [self loginToFacebook];
    }
}</code>

 

Но теперь следует изменить метод, в которых приходят данные от сервера:

<code data-result="[object Object]">- (void)request:(FBRequest *)request didLoad:(id)result {
    if ([result objectForKey:@"name"]) {
        NSString *userName = [result objectForKey:@"name"];
        NSUserDefaults	*defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:userName forKey:@"userName"];
        [defaults synchronize];

        [fbButton setTitle:userName forState:UIControlStateNormal];
    } else if ([result objectForKey:@"data"]) {
        NSArray *curentResult = [result objectForKey:@"data"];

        for (NSDictionary *element in curentResult) {
            NSLog(@"id:%@", [element objectForKey:@"id"]);
            NSLog(@"name:%@", [element objectForKey:@"name"]);
            NSLog(@"photoURL:%@", [element objectForKey:@"picture"]);
            NSLog(@"gender:%@", [element objectForKey:@"gender"]);
        }
    }        
}</code>

 

Как видите, нам пригодилась проверка типа данных.

С помощью последнего метода мы будем добавлять в профиль фотографию:

<code data-result="[object Object]">- (IBAction)uploadPhoto {
    [self initFacebook];

    if () {    
        UIImage *img  = [UIImage imageNamed:@"logo.png"];

        NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                       img, @"picture", nil];
        ;
    } else {
        [self loginToFacebook]; 
    }    
}</code>

 

Кроме этих методов советую в реализацию класса facebookAppViewController добавить следующий метод делегата Facebook:

<code data-result="[object Object]">- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
    NSLog(@"%@", error);
}</code>

 

Это своего рода подсказка, он будет выводить в консоль текст ошибки, если вы создадите неправильно какой-то запрос.

Как и во всех случаях, с примерами я добавлю к статье исходный код. Но ID приложения с этого кода я удаляю, на его место вам следует подставить свой ID.

Дополнительно:

Недавно мне пришлось разобраться с тем, как загружать видео на Facebook. Этим примером я хочу поделиться с вами. Чтобы загрузка видео успешно работала вам прийдется воспользоваться мною измененным APIFacebook. Как я уже писал, скачать его можно отсюда. Естественно, нам нужен видео файл, над которым мы будем экспериментировать. Я предлагаю скачать его отсюда. Но вы можете воспользоваться любым другим файлом.

Теперь добавьте в интерфейс класса facebookAppViewController еще один метод uploadVideo, добавьте в xib-файл кнопку и свяжите нажатия на эту кнопку с вызовом метода uploadVideo. Теперь напишем реализацию этого метода.

<code data-result="[object Object]">- (IBAction)uploadVideo {
    [self initFacebook];

    NSString *moviePath = [[NSBundle mainBundle] pathForResource:@"sample_mpeg4" ofType:@"mp4"];
    NSURL *movieURL = [NSURL fileURLWithPath:moviePath];

    NSDictionary * params = [NSDictionary dictionaryWithObjectsAndKeys:
                             @"Sample video title", @"title",
                             @"Sample video description", @"description",
                             nil];

    FBVideoUpload *upload = [[FBVideoUpload alloc] init];
    upload.accessToken = facebook.accessToken;
    upload.apiKey = @"Set App Key";
    upload.appSecret = @"Set App Secret";
    [upload startUploadWithURL:movieURL params:params delegate:self];
}</code>

 

ApiKey и AppSecret я частично спрятал, на их место вам нужно подставить свои, которые вы получили при создании приложения на сайте Facebook. Кроме добавленного метода нам прийдется изменить метод request didLoad:

<code data-result="[object Object]">- (void)request:(FBRequest *)request didLoad:(id)result {
    if ([result isKindOfClass:[NSDictionary class]]) {
        if ([result objectForKey:@"name"]) {
            NSString *userName = [result objectForKey:@"name"];
            NSUserDefaults	*defaults = [NSUserDefaults standardUserDefaults];
            [defaults setObject:userName forKey:@"userName"];
            [defaults synchronize];

            [fbButton setTitle:userName forState:UIControlStateNormal];
        } else if ([result objectForKey:@"data"]) {
            NSArray *curentResult = [result objectForKey:@"data"];

            for (NSDictionary *element in curentResult) {
                NSLog(@"id:%@", [element objectForKey:@"id"]);
                NSLog(@"name:%@", [element objectForKey:@"name"]);
                NSLog(@"photoURL:%@", [element objectForKey:@"picture"]);
                NSLog(@"gender:%@", [element objectForKey:@"gender"]);
            }
        }        
    } else {
        NSString *XMLresult = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
        NSLog(@"%@", XMLresult);
        [XMLresult release];
    }
}</code>

 

Поскольку значение result может принимать любой тип — я добавил проверку типа. Для этого я воспользовался методом isKindOfClass. В случае если тип данных NSDictionary — выполняем все как и прежде. Иначе — конвертируем данные в строку и выводим ее в консоль.

Исходный код можно скачать здесь.

<code data-result="[object Object]"><!--nextpage-->
</code>

Twitter

Этот метод работы с Twitter уже не работает. Вскоре статья будет обновлена.

 

Продолжая тему социальных сетей хочу привести пример работы с Twitter. Как и в случае с Facebook, в первую очередь нам понадобиться учетная запись в этой социальной сети. Если она не имеется — следует зарегестрироваться. После успешной регистрации нам понадобиться API Key. Для этого зайдите на страничку своего профиля. Затем в правой нижней части странички перейдите по ссылке Developers.

Таким образом вы попадете на страничку для разработчиков. В нижней части этой странички будет ссылка API кликните по ней.

В открывшейся страничке нажмите на кнопку Register an app.

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

Если все сделано правильно — система вас перекинет на страничку, где вам будут предоставлены ключи доступа и другая полезная информация. Нас интересуют поля Consumer key и Consumer secret. Как раз значения этих полей нам нужны для работы с APITwitter. Сохраните их где-то на компьютере для дальнейшего использования в приложении.

Теперь создадим сам проект на основе представления (View-based Application) и назовем его twitterApp. Поскольку для работы с Twitter мы будем использовать готовые библиотеки — их нужно добавить в проект. Скачать эти библиотеки можно отсюда. Кроме библиотеки нам нужно добавить в проект фреймверк libxml2.dylib (как это сделать описано здесь). Но так просто этот фреймверк не будет работать. Нужно проделать некоторую магию. Проделайте действия указанные стрелками на картинке.

В поле Header Shearch Paths следует вписать строку $(SDKROOT)/usr/include/libxml2. Только после этого можно использовать фреймверк libxml2.dylib (в противном случае приложение не будет запускаться).

Теперь приступим к процессу программирования. Исправим интерфейс класса twitterAppViewController.

Добавте в файл twitterAppViewController.xib кнопку и свяжите действие нажатия на эту кнопку с вызовом метода loginToTwitter.

Изменим реализацию класса twitterAppViewController соответственно интерфейсу:

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

Теперь при нажатии на только что добавленную кнопку вы увидите следующую картинку:

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

Это методы делегата и в интерфейс их добавлять не нужно. Метод storeCachedTwitterOAuthData вызывается в момент кеширования данных сессии, в нем мы сохраняем эти данные в UserDefaults. Метод cachedTwitterOAuthDataForUsername вызывается в момент попытки авторизации. Если в этом методе мы возвращаем пустое значение — открывается окно авторизации, в противном случае — сессия восстанавливается с тех данных, который мы возвращаем в этом методе.

Кроме этого, многим будет полезный метод authenticatedWithUsername, который вызывается в момент успешной авторизации пользователя. Отображать имя пользователя я решил на кнопке логина (как и в случае с Facebook). Для этого я добавил в интерфейс класса twitterAppViewController переменную twButton типа UIButton и связал ее с кнопкой в xib-файле. В реализации класса twitterAppViewController я изменил метод viewDidLoad.

В нем мы устанавливаем надпись кнопки с ранее сохраненного имени пользователя.

И сам метод authenticatedWithUsername, в котором делаем практически то же самое:

Добавим возможность выхода из системы. Для этого изменим метод loginToTwitter.

Теперь, перед тем как залогиниться мы проверяем валидность сессии. Если сессия валидна — удаляем всю информацию о ней, иначе — открываем окно авторизации.

Добавим в интерфейс класса twitterAppViewController еще два метода (postText и updateStream). Так же для этих методов добавьте две кнопки, при нажатии на которые будут вызываться эти методы.

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

В нем мы инициализируем твиттер, проверяем сессию на валидность. Если сессия валидна — постим текст, иначе — открываем окно авторизации.

В методе updateStream мы отправляем запрос для получения ленты с текстом.

Когда данные прийдут от сервера — будет вызван метод делегата statusesReceived:

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

Если такой способ получения списка друзей не подходит — можно воспользоваться прямой ссылкой на сервер Twitter:
https://api.twitter.com/1/statuses/friends.xml?screen_name=userName
Где userName это имя пользователя, список друзей которого вы хотите получить. Сервер вам вернет *.xml-файлик, который нужно скачать и распарсить, как мы это делали в примере с RSS.

Это основные функции работы с API Twitter. Исходный код можно скачать здесь. Кроме этого я хочу обратить ваше внимание на пример, который вдохновил меня на написание этой статьи. Скачать его можно здесь.