Разработка под Apple. Работа с API «В контакте»

В этой статье мы отойдём от классической схемы сначала теория, а потом практика и сразу перейдём к написанию приложения. Теория также будет присутствовать, но объяснения будут даваться по ходу написания приложения, точнее, части приложения. В этой статье мы рассмотрим авторизацию для приложений социальной сети «В контакте». Авторизация возможна несколькими способами: официальный и наиболее безопасный для пользователя — использование компонента браузера для перехода на страницу разрешения доступа на сайте Контакта; также можно обойтись без компонента браузера, но в данном случае мы получаем непосредственный доступ к логину и паролю пользователя, что ставит под угрозу его безопасность. Хоть мы и не злоумышленники, но пользоваться будем первым способом.

Разработка под Apple. Работа с API «В контакте». Фото.

Для начала будет полезно ознакомиться с официальной документацией на сайте социальной сети.

Итак, как следует из документации, чтобы начать нам нужно зарегистрировать своё приложение на странице https://vkontakte.ru/apps.php?act=add.

Создание нового приложения вконтакте

Рис. 1. Создание нового приложения вконтакте

После нажатия «Перейти к загрузке приложения» мы попадем на страницу настроек нашего приложения (в дальнейшем эта страница будет доступна Приложения > Настройки > Администрируемые приложения).

Страница настроек созданного приложения вконтакте

Рис. 2. Страница настроек созданного приложения вконтакте

Для того, чтобы сформировать запрос авторизации нам нужны client_id (ID приложения, см. в настройках), scope (права доступа), redirect_uri (адрес, на который будет передан access_token, в нашем случае https://api.vkontakte.ru/blank.html), display (внешний вид окна авторизации, в нашем случае touch, так как мы будем писать авторизацию для iPhone). Для авторизации standalone приложений не требуется передавать защищенный ключ, поэтому никогда не указывайте ваш секретный ключ внутри кода приложения из-за возможности его декомпиляции и использования злоумышленниками.

В итоге наш запрос будет выглядеть так:

https://api.vkontakte.ru/oauth/authorize?client_id=НАШ_ID&scope=video&redirect_uri=https://api.vkontakte.ru/blank.html&display=touch&response_type=token

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

Откроем, наконец-то, Xcode и создадим новое Window-Based Application (мы создадим Контроллеры Вида сами). Назвать можете его произвольно, своё я назвал VK.

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

Авторизация в приложении вконтакте

Рис. 3. Авторизация в приложении вконтакте

Возможно это не самый лучший способ авторизации для приложения, но наша цель изучить как можно больше приёмов работы с iOS SDK.

Первое, что мы сделаем в нашем приложении — создадим Контроллер и Вид для авторизации, для этого нажмём Cmd + N и выберем UIViewController subclass в вкладке Cocoa Touch. После того, как нажмёте Next, не забудьте отметить With XIB for user interface, чтобы Xcode создал для нас файл вида (.xib). Создать Вид с помощью кода также возможно в методе — (void)viewDidLoad. Мы рассмотрим его, когда будем создавать страницу с текстом о необходимости авторизации.

Назовём наш новый класс “AuthViewController”.

UIViewController subclass

Рис. 4. UIViewController subclass

Откроем AuthViewController.h и добавим Протокол, который реализует наш контроллер. Мы впервые познакомились с понятием Протокола, если кратко, то Протокол представляет собой список описаний методов. Объект реализует протокол, если он содержит реализации всех методов, описанных в протоколе и обязательных для реализации. Протоколы удобны тем, что позволяют выделять общие черты у разнородных объектов и передавать информацию об объектах заранее неизвестных классов.

Мы не будем создавать свой протокол, а используем протокол UIWebViewDelegate (напомню, что для авторизации мы будем использовать компонент браузера UIWebView и данный протокол содержит описание методов, которые мы реализуем):

//
// AuthViewController.h
// VK
//
// Created by Vitaly Ishkulov on 10/5/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <uikit /UIKit.h>

@interface AuthViewController : UIViewController <uiwebviewdelegate> {

}

@end</uiwebviewdelegate></uikit>

Далее добавим объекты и свойства для них:

@interface AuthViewController : UIViewController <uiwebviewdelegate> {
UIWebView *authView;
UIActivityIndicatorView *indicator;
}

@property (nonatomic, retain) IBOutlet UIWebView *authView;
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *indicator;

@end</uiwebviewdelegate>

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

Создадим интерфейс в AuthViewController.xib файле. Добавим Web View:

Web View

Рис. 5. Web View

так, чтобы он был на весь экран айфона. Далее добавим Activity Indicator View. Вот, что у нас должно получиться в итоге:

Activity Indicator View

Рис. 6. Activity Indicator View

Теперь нужно добавить связи (напомню, что связи добавляем удерживая Ctrl и перетягивая от File’s Owner к Outlets и наоборот от Actions к File’s Owner.

Нам нужно соединить:

WebView –> File’s Owner (выбрать delegate);
File’s Owner –> File’s Owner (выбрать authView);
File’s Owner –> Activity Indicator (выбрать indicator).

Теперь можем перейти к реализации (AuthViewController.m). Генерируем getter и setter с помощью

@synthesize authView, indicator;

Перейдём к методу –viewDidLoad. Проверка авторизован пользователь или нет будет проходить в фоновом режиме, и окно авторизации пользователь увидит только если он не авторизован, поэтому мы скроем authView:

- (void)viewDidLoad
{
[super viewDidLoad];
authView.hidden = YES;
}

Далее нам нужно сформировать запрос и отправить его на сервер Контакта. Запрос мы формировали в начале статьи и в виде строки он выглядит так:

@"https://api.vkontakte.ru/oauth/authorize?client_id=НОМЕР_ID_КЛИЕНТА&scope=video&redirect_uri=https://api.vkontakte.ru/blank.html&display=touch&response_type=token"

Не забудьте поменять НОМЕР_ID_КЛИЕНТА на ID вашего приложения, который показан в настройках приложения на сайте Контакта.

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

NSString *authString = [[NSString alloc] initWith-String:@"https://api.vkontakte.ru/oauth/authorize?client_id=2464430&scope=video&redirect_uri=https://api.vkontakte.ru/blank.html&display=touch&response_type=token"];
NSURL *authURL = [[NSURL alloc] initWithString:authString];
[authString release];
NSURLRequest *authRequest = [[NSURLRequest alloc] initWithURL:authURL];
[authURL release];
[authView loadRequest:authRequest];
[authRequest release];

Не забываем удалять из памяти созданные объекты. –loadRequest: метод UIWebView, который отправляет запрос на сервер контакта и рендерит результат в компоненте браузера, который у нас скрыт. Всё что нам нужно — это отследить URI, которая в настоящее время загружена в Web View и в зависимости от этого показать Web View с формой авторизации или не показывать и перейти к самому приложению передав ему данные об авторизации (secret).

Вот сейчас нам и пригодится Протокол UIWebViewDelegate, который реализуется нашим контроллером, в частности мы используем метод

- (void)webViewDidFinishLoad:(UIWebView *)webView, который выполняется после того, как загрузка страницы в компоненте браузера завершена:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([authView.request.URL.absoluteString rangeOfString:@"access_token"].location != NSNotFound) {
authView.hidden = YES;
NSString *secret = [authView.request.URL.absoluteString getStringBetweenString:@"access_token" andString:@"&"]; //извлекаем из ответа token
NSLog(@"%@", secret); //печатаем secret в консоль
} else if ([authView.request.URL.absoluteString rangeOfString:@"error"].location != NSNotFound) {
authView.hidden = YES;
NSLog(@"%@", authView.request.URL.absoluteString); //выводим ошибку
} else {
authView.hidden = NO; //показываем окно авторизации
}
[indicator stopAnimating];
}

В методе мы используем простую проверку текущего URI компонента браузера и в зависимости от того, содержится там access_token или нет выполняем действия. Также мы отлавливаем ошибку и печатаем её в консоль. Ещё один примечательный метод

- (NSString *)getStringBetweenString:(NSString *)first andString:(NSString *)second;

позволяет извлечь строку access_token из ответа сервера Контакта.

Данный метод реализован с помощью Категории (Category) — механизм добавления собственных методов к существующим классам (в нашем случае к NSString). Подробнее о категориях можете прочитать в документации. Чтобы добавить новый метод к классу NSString нажмём Cmd+N и выберем из списка Objective-C Category. Назовём её StringBetween и выберем в качестве класса категории NSString. Далее добавим наш метод в заголовок (StringBetween.h):

#import
@interface NSString (StringBetween)
- (NSString *)getStringBetweenString:(NSString *)first andString:(NSString *)second;
@end

и реализацию в файл StringBetween.m:

#import «StringBetween.h»

@implementation NSString (StringBetween)

- (NSString *)getStringBetweenString:(NSString *)first andString:(NSString *)second {
NSRange rangeofFirst = [(NSString *)self rangeOfString:first];
NSRange rangeOfSecond = [(NSString *)self rangeOfString:second];
if ((rangeofFirst.length == 0) || (rangeOfSecond.length == 0)) {
return nil;
}
NSString *result = [[(NSString *)self substringFromIndex:rangeofFirst.location+rangeofFirst.length]
substringToIndex:
[[(NSString *)self substringFromIndex:rangeofFirst.location+rangeofFirst.length] rangeOfString:second].location];
return result;
}
@end

Теперь не забудьте добавить #import «StringBetween.h» в заголовок нашего контроллера AuthViewController.h.

Ещё один примечательный момент как мы используем self для обращения к объекту, которому посылается метод. Чтобы компилятор не ругался мы принудительно указываем класс данного объекта (NSString *)self (это называется ‘cast’).

Вернёмся к контроллеру авторизации. Его реализация не завершена, так как мы никак не используем полученный secret. По логике приложения мы перейдём к новому виду (и новому контроллеру) после того как успешно авторизуемся.

Для работы с несколькими контроллерами в Obj-C используются контроллеры контроллеров. Их несколько, сегодня мы познакомимся с Navigation Controller, наверное, самым часто используемом в iOS.

Наше приложение мы начали писать без использования готового каркаса (мы выбрали Window-Based App), поэтому если мы сейчас его запустим, то ничего не произойдёт. Нам нужно добавить наш View в иерархию видов. Добавим #import «AuthViewController.h» в VKAppDelegate.h.

Откроем VKAppDelegate.m и добавим следующие строки в метод didFinishLaunching (в комментариях описано что делает каждая строка):

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UINavigationController *navigation = [[UINavigationController alloc] init]; //создаём контроллер навигации
AuthViewController *auth = [[AuthViewController alloc] init]; //создаём вид авторизации
[navigation pushViewController:auth animated:NO]; //передаём вид авторизации и показываем его без анимации, так как при первом появлении нам анимация ни к чему.
[auth release]; //мы передали инстанс auth под управление навигационного контроллера и поэтому можем от него избавиться
[_window addSubview:navigation.view]; //добавляем навигационный контроллер в наш вид
[self.window makeKeyAndVisible]; //
return YES;

Теперь мы можем запустить наше приложение:

Смотрим что получилось на iPhone

Рис. 7. Смотрим что получилось на iPhone

Отлично, мы видим страницу авторизации Контакта, если мы авторизуемся, далее откроется страница с запросом прав доступа для нашего приложения, мы нажмём «Разрешить», secret напечатается в консоль и… больше ничего не произойдёт. Если же мы повторно откроем приложение, то вообще ничего не произойдёт, кроме как secret напечатается в консоль. А нам нужно, чтобы открылся Вид приложения и secret передался контроллеру данного вида.

Реализовать это достаточно просто.

Создадим по аналогии ещё один subview и назовём его VKAppViewController. Откроем VKAppViewController.xib, добавим элемент UILabel. Вызовем окно Ассистента Cmd+Opt+Return. С нажатой клавишей Ctrl перетянем от UILabel, которую мы поместили в интерфейс айфона в файл VKAppViewController.h, который открыт в Ассистенте. Назовите объект label и нажмите Connect. Xcode сам создаст аутлет, свойство для него и позаботится об освобождении памяти (работает только в Xcode 4).

Автоматически генерируем аутлет, свойство для UILabel

Рис. 8. Автоматически генерируем аутлет, свойство для UILabel

Мы закончили с VKAppViewController, осталось передать ему secret и отобразить его в label.

Откроем файл AuthViewController.m и создадим метод -sendString:

- (void)sendString:(NSString *)secret {
VKAppViewController *app = [[VKAppViewController alloc] init]; //создаем инстанст «приложения» (VKAppViewControl-ler)
app.title = [NSString stringWithFormat:@"Application"]; //устанавливаем заголовое для него
[self.navigationController pushViewController:app animated:YES]; //передаём управление контроллеру VKApp и загружаем вид
app.label.text = [NSString stringWithFormat:@"%@", secret]; //отображаем в label токен авторизации приложения (или выводим ошибку)
[app release]; //мы передали управление контроллеру навигации, поэтому владеть контроллером VKApp нам больше не нужно
}

Теперь в методе –webViewDidFinishLoad: допишем две строки:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([authView.request.URL.absoluteString rangeOfString:@"access_token"].location != NSNotFound) {
authView.hidden = YES;
NSString *secret = [authView.request.URL.absoluteString getStringBetweenString:@"access_token=" andString:@"&"]; //извлекаем из ответа token
[self sendString:secret]; //передаём строку с secret
} else if ([authView.request.URL.absoluteString rangeOfString:@"error"].location != NSNotFound) {
authView.hidden = YES;
[self sendString:authView.request.URL.absoluteString]; //передаём ошибку
} else {
authView.hidden = NO;
}
[indicator stopAnimating];
}

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

Задание

В качестве домашнего задания попробуйте написать простенький браузер, который будет иметь адресную строку, кнопки вперёд/назад. Посмотрите документацию по UIWebView.