0,0
рейтинг
25 июня 2013 в 13:10

Разработка → ВКонтакте iOS SDK из песочницы

Добрый вечер!

Всё началось с того, что необходим был более или менее удобный инструмент для работы с API социальной сети ВКонтакте под iOS. Однако Google меня достаточно быстро расстроил результатами поиска:

Вроде бы всё хорошо, самое главное есть, но вот использование не вызывает приятных ощущений.

Под катом я расскажу, как работает новая версия ВКонтакте iOS SDK, с чего всё начиналось и к чему в итоге пришли.


Предпроект


Началось всё с того, что на работе получили задание подключить к приложению социальные сети. Мы хотели, чтобы пользователь не заметил никакой разницы при взаимодействии с разными социальными сетями (постинг фотографии на стену, отправка сообщения, загрузка фотографий и т.п.).
Было решено поискать готовые решения, которые бы содержали в себе несколько социальных сетей вроде ВКонтакте, Одноклассники, Твиттер и Фэйсбук, но ничего не удалось найти. Использование готовых решений по отдельности не давало нужных результатов, поэтому решили писать свои велосипеды, предварительно изучив Facebook iOS SDK, MGTwitterEngine и несколько других приметных библиотек.

В итоге мы получили ASASocialServices (GitHub).
Проект получился простым в использовании и установке, большее внимание уделялось работе с Twitter и Vkontakte, на Facebook было решено не концентрироваться.

В ASASocialServices работа с тремя социальными сетями (далее речь будет идти только о двух) осуществлялась по единому принципу: программист создаёт UIWebView, позиционирует его и отображает, затем запускает процесс авторизации пользователем приложения и, в зависимости от принятого пользователем решения, вызывается один из трёх блоков-обработчиков (success, error, cancel).

Если рассматривать в контексте, то ViewController.h выглядит примерно так:
#import <UIKit/UIKit.h>
#import "ASASocialServices.h"

@interface ViewController : UIViewController

@property UIWebView *webView;
@property ASATwitterCommunicator *tw;

@end


ViewController.m
#import "ViewController.h"

NSString *const kTWITTER_CONSUMER_KEY = @"v8146mdwpo05uEroMnhozg";
NSString *const kTWITTER_CONSUMER_SECRET = @"5AFkvjCKxqGBRId2fpSQFLClJLKtGcPGS1DzK7o";

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // устанавливает WebView в нужной позиции и с нужными размерами
    CGRect frame = [[UIScreen mainScreen] bounds];
    _webView = [[UIWebView alloc] initWithFrame:frame];
    [self.view addSubview:_webView];

    // создаем TwitterCommunicator для получения токенов
    _tw = [[ASATwitterCommunicator alloc]
            initWithWebView:_webView];

    // инициируем запрос по получению доступа к пользовательскому аккаунту
    [_tw startOnCancelBlock:^{
        NSLog(@"User canceled app authorization...");
    } onErrorBlock:^(NSError *error) {
        NSLog(@"error during app authorization...%@", error);
    } onSuccessBlock:^(ASATwitterUserAccount *account) {
        NSLog(@"Account: %@", account);
    }];
}

@end


Достаточно было заменить ASATwitterCommunicator на ASAVkontakteCommunicator или ASAFacebookCommunicator, чтобы подключить и начать работать с другой социальной сетью.
В последний блок — success, происходила передача пользовательской учетной записи соответствующей сети (токен доступа, идентификатор пользователя, время истечения действия токена доступа и т.д.)

Последующие запросы от лица текущего пользователя можно было производить таким образом:
[account performTwitterMethod:kTWITTER_FOLLOWERS_LIST_URL
                           HTTPMethod:@"GET"
                              options:@{@"user_id" : account.twitterUserID,
                                        @"include_entities": @"true"}
                              success:^(id response) {
                                  NSLog(@"response: %@", response);
                              }
                              failure:^(NSError *error) {
                                  NSLog(@"error: %@", error);
                              }];


Вот как выглядит обновление статуса пользователя в Twitter:
[account performTwitterMethod:kTWITTER_STATUSES_UPDATE_URL
                           HTTPMethod:@"POST"
                              options:@{@"status": @"Hello from ASASocialServices Framework!"}
                              success:^(id response) {
                                  NSLog(@"response: %@", response);
                              } failure:^(NSError *error) {
                                  NSLog(@"error: %@", error);
                              }];


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

Минусы:
  • Программисту необходимо было помнить, что токен доступа можно сохранить и использовать в последующих запросах, а не вешать запросы в success-блок
  • Оставлять на программиста настройку и работу с UIWebView было тоже ошибкой
  • Библиотека казалась слишком сложной и непонятной, большинству не хотелось думать о том POST или GET использовать при запросах


ВКонтакте iOS SDK v1.0


С  ASASocialServices мне больше не хотелось возиться, поэтому решил, что начну писать в свободное время SDK для ВКонтакта. Набросал на листке схему взаимодействия классов, дня два над ней «висел», в итоге решил, что на первую версию похоже — приступил к реализации.

Я люблю Ruby и мне нравятся Rails и, почему-то всегда и до сих пор кажется, что именно они в некоторой степени повлияли на вид Вконтакте iOS SDK.

Пользователь связан с такими объектами, как:
  • Группы
  • Стена
  • Друзья
  • Аудио альбомы
  • Видео альбомы
  • Фотоальбомы
  • Записи
  • Документы
  • тд


У каждого объекта есть список действий, который пользователь может осуществить от своего лица:
  • Создать фотоальбом
  • Вступить в группу
  • Установить/изменить статус
  • Получить список друзей, которые сейчас на сайте
  • и тд


Вот, как приведенные выше действия будут выглядеть во ВКонтакте iOS SDK v1.0:

Создание фотоальбома:
    VKUser *me = [VKUser currentUser];
    [[me photoAlbums] createTitle:@"Привет, Хабр!" description:@"Альбом с фотографиями для Хабра"];

image

Вступить в группу:
    VKUser *me = [VKUser currentUser];
    [[me groups] joinGroupID:100500];


Установить статус:
    VKUser *me = [VKUser currentUser];
    [[me status] setStatus:@"Привет, Хабр!"];


Получить список друзей, которые сейчас на сайте:
id result = [[[VKUser currentUser] friends] online];


С чего начать?

Предположим, что Вы уже добавили Vkontakte IOS SDK v1.0 к себе в проект и не знаете что делать дальше с этим.

Мы будем работать с классом VKConnector, который позволит нам получит+сохранить+использовать полученный токен доступа единожды, а в нужный момент он уведомит нас, что необходимо обновить токен и вызовет соответствующий метод делегата, который может следовать (а может и нет) VKConnectorProtocol.

Вот, как будет выглядеть самый простой способ подключения в ASAAppDelegate.m:
//
//  ASAAppDelegate.m
//  Vkontakte iOS SDK_Project
//
//  Created by AndrewShmig on 05/27/13.
//  Copyright (c) 2013 AndrewShmig. All rights reserved.
//

#import "ASAAppDelegate.h"
#import "ASAViewController.h"
#import "VKUser.h"


static NSString *const kVKAppID = @"3541027";
static NSString *const kVKPermissionsArray = @"photos,friends,wall,audio,video,docs,notes,pages,status,groups,messages";


@implementation ASAAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    [[VKConnector sharedInstance] setDelegate:self];
    [[VKConnector sharedInstance] startWithAppID:kVKAppID
                                      permissons:[kVKPermissionsArray componentsSeparatedByString:@","]];

    // Override point for customization after application launch.
    self.viewController = [[ASAViewController alloc] initWithNibName:@"ASAViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)VKConnector:(VKConnector *)connector willShowModalView:(KGModal *)view
{
    NSLog(@"%s", __FUNCTION__);
}

- (void)VKConnector:(VKConnector *)connector willHideModalView:(KGModal *)view
{
    NSLog(@"%s", __FUNCTION__);
}

- (void)VKConnector:(VKConnector *)connector accessTokenInvalidated:(VKAccessToken *)accessToken
{
    NSLog(@"%s", __FUNCTION__);
}

- (void)VKConnector:(VKConnector *)connector accessTokenRenewalFailed:(VKAccessToken *)accessToken
{
    NSLog(@"%s", __FUNCTION__);
}

- (void)VKConnector:(VKConnector *)connector accessTokenRenewalSucceeded:(VKAccessToken *)accessToken
{
    NSLog(@"%s", __FUNCTION__);
}

- (void)VKConnector:(VKConnector *)connector connectionErrorOccured:(NSError *)error
{
    NSLog(@"%s", __FUNCTION__);
}

- (void)VKConnector:(VKConnector *)connector parsingErrorOccured:(NSError *)error
{
    NSLog(@"%s", __FUNCTION__);
}

@end


После запуска перед пользователем возникает примерно такое модальное окно (использовался KGModal) для авторизации приложения:
image

Если возникают вопросы или вы не знаете для чего нужен (за что отвечает) тот или иной метод, то обращайтесь смело к документации. Документация сгенерирована при помощи AppleDoc и выглядит в целом следующим образом:
image

VKConnectorProtocol:
image

XCode поможет в этом:
image

В завершение


Статья, как мне кажется, получилась достаточно длинной, так что на этом пока остановлюсь, хотя к сожалению многое не упомянул из того, что планировал (загрузка файлов, обновление токена, обработка ошибок и тд)

Хочу отметить, что проект активно развивается и поддерживается. Текущий статус проекта — «Готов», поэтому в v1.0 будут только исправляться ошибки и вноситься мелкие коррективы. Все глобальные изменения переносятся в v2.0.

Найти самую актуальную версию можно по этой ссылке: GitHub ( https://github.com/AndrewShmig/Vkontakte-iOS-SDK )

Некоторую информацию по Vkontakte iOS SDK v2.0, можно найти здесь: GitHub (https://github.com/AndrewShmig/Vkontakte-iOS-SDK-v2.0/issues?labels=Future+features&state=open)

Благодарю за внимание.
Андрей Шмиг @AndrewShmig
карма
15,0
рейтинг 0,0
В поиске…
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (15)

  • 0
    Большое спасибо за библиотеку.
    Недавно столкнулся с похожей проблемой, нужно было сделать авторизацию пользователя через вк, постинг, получение информации от друзьях.
    Так как времени на реализацию было не много, воспользовался StonewHawk's версией и допилил под свои нужды.
    • 0
      Благодарю за отзыв!
      Надеюсь Вам приятно будет использовать SDK.
  • +1
    Пара TODO-шек:

    — заверните это в CocoaPods
    — обновите сырцы. У вас много «старых» вещей: @synthesize'ы, ivar'ы для свойств, id место instancetype
    — (сосем уж субъективно) и если можно, то сделайте зеркало на гитхабе, чтоб можно было вам помочь используя более привычные средства ;)

    Ну и спасибо вам, конечно же :)
    • +1
      1) Сделаем
      2) Понял, подкорректируем
      3) Так и знал, что надо было оставлять проект на GitHub, а не переводить на BitBucket. В ближайшее время перенесу.

      Благодарю за отзыв!
    • 0
      «сосем уж субъективно» :)
  • 0
    Не совсем понятно, как работают коллбэки.
    Например

    VKUser *me = [VKUser currentUser];
    [[me groups] joinGroupID:100500];
    

    подразумевает асинхронный запрос, как узнать что он закончился?
    • –1
      Запросы не асинхронные, это одна из особенностей первой версии.
      VKUser *me = [VKUser currentUser];
      id serverResponse = [[me groups] joinGroupID:100500];
      
      NSLog(@"serverResponse: %@", serverResponse);
      
      • 0
        Я бы сказал, что это критичная особенность первой версии. А вообще чаще посматривайте на FB SDK и будет Вам счастье.
        • 0
          Я так понимаю, что на слове «критичная» делается упор, поэтому хотелось бы услышать почему это настолько критично (я прекрасно понимаю, что при загрузке файлов — это еще может доставить какие-то неудобства, но при осуществлении простых запросов...)?
          Просто прошу привести пример, не более.
          • 0
            Простой запрос может затянуться по разным причинам, и будет лаг в интерфейсе, который неприемлем. При любой работе в меин треде будет этот лаг, как быстро бы запрос не исполнялся, и это раздражает.
            • 0
              А что мешает делать хотя бы так:
              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
                 id response = [[[VKUser currentUser] friends] online];
              
                 // обработка и бла-бла-бла
              });
              


              PS
              Во второй версии данного «косяка» не будет — блоки и делегаты вводятся.
  • 0
    От себя пожелание — кеш запросов в версии 2.0 реализуйте, тем более что AFNetworking собрались исползовать.
    Кстати будет только блочная модель у вас или старые добрые делегаты тоже будут?
    • +2
      Я бы тогда попросил Вас предложения на ГитХаб писать, там я про них точно не забуду и можно будет отслеживать обязательность/важность вводимых изменений. Если такой возможности нет — ничего страшного.

      Что касается блоков как таковых, то пока ничего не могу точно сказать. По опыту из ASASocialServices могу сказать, что блоки не вписываются в моё представление о красивой SDK и удобстве (мне очень не нравится глубокая вложенность блоков, больше 1-2 уровней), но это не окончательно, поэтому будем думать и насчет делегатов.
  • 0
    Отличная работа. А почему второй версии нет в GitHub?

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