Подключаем ВКонтакте SDK для Xamarin.Forms

В прошлой статье мы рассмотрели вопрос с подключением нативных SDK от Facebook в ваших приложениях на Xamarin.Forms для удобной авторизации пользователей. Сегодня, как и обещали, рассмотрим подключение нативных SDK для социальной сети ВКонтакте. Новый SDK будет подключаться к проекту, который мы описывали в прошлой статье.

66c4496b45134cf78428b8671f8dce35.jpg

Создаем приложение во ВКонтакте


В целом, процесс интеграции ВКонтакте будет сильно напоминать работу с Facebook, так что смело заходите на страницу управления приложениями.

Нажимаем «Создать приложение» и выбираем «Standalone-приложение».

9bddcb2d34b447c7911b8b5f5c2bf71a.png

Далее идём в Настройки и вводим данные о приложении. «Отпечаток сертификата» это Key Hashes, полученные нами в прошлой статье.

733ef17348884c34aef9e185b75616f4.png

На этом подготовительная часть завершена.

Подключаем ВКонтакте SDK к проектам iOS и Android


Для Xamarin доступно достаточно много готовых bindings, однако полноценный ВКонтакте SDK появился совсем недавно. Библиотека какое-то время пребывала в стадии beta и сейчас готова к использованию. Большое спасибо Matthew Leibowitz!

f797fd50937341658af917ad839506a7.png

Подключаем в iOS


Вносим правки в Info.plist. Расширяем CFBundleURLTypes значениями для ВКонтакте:
CFBundleURLTypes

  
	  CFBundleURLName
	  vk5874073
    CFBundleURLSchemes
    
      fb1102463466549096
      vk5874073
    
  

Добавляем новые LSApplicationQueriesSchemes:
  vk 
  vk-share 
  vkauthorize

И также новый домен в NSAppTransportSecurity:
vk.com 
       
          NSExceptionRequiresForwardSecrecy 
           
          NSIncludesSubdomains 
           
          NSExceptionAllowsInsecureHTTPLoads 
           
      

После этого вносим правки в AppDelegate.cs.
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            Xamarin.Forms.Forms.Init();

            LoadApplication(new App());

            Facebook.CoreKit.Profile.EnableUpdatesOnAccessTokenChange(true);
            Facebook.CoreKit.ApplicationDelegate.SharedInstance.FinishedLaunching(app, options);
            
            VKSdk.Initialize("5874073");

            return base.FinishedLaunching(app, options);
        }

        public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
        {
            return VKSdk.ProcessOpenUrl(url, sourceApplication) 
                || Facebook.CoreKit.ApplicationDelegate.SharedInstance.OpenUrl(application, url, sourceApplication, annotation) 
                || base.OpenUrl(application, url, sourceApplication, annotation);
        }

На этом первичная инициализация iOS завершена.

Подключаем в Android


А вот для Android придётся дополнительно переопределить свой класс Application для корректной инициализации SDK.
    [Application]
    public class MainApplication : Application
    {
        public MainApplication(IntPtr handle, JniHandleOwnership transer)
          :base(handle, transer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();
            VKSdk.Initialize(this).WithPayments();
        }
    }

Теперь добавим ID приложения в strings.xml:
  5874073
  vk5874073

Добавим немного кода в AndroidManifest.xml между :

    
      
        
        
        
        
      
    

И завершим расширением MainActivity:
protected override async void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            bool vkResult;
            var task = VKSdk.OnActivityResultAsync(requestCode, resultCode, data, out vkResult);

            if (!vkResult)
            {
                base.OnActivityResult(requestCode, resultCode, data);
                AndroidFacebookService.Instance.OnActivityResult(requestCode, (int)resultCode, data);
                return;
            }

            try
            {
                var token = await task;
                // Get token
            }
            catch (Exception e)
            {
                // Handle exception
            }
        }

Интегрируем с Xamarin.Forms


По аналогии с Facebook, мы создадим свой интерфейс в PCL-проекте для работы с новым SDK.
    public interface IVkService
    {
        Task Login();
        void Logout();
    }

Реализация для iOS


Для iOS реализация будет выглядеть следующим образом:
[assembly: Dependency(typeof(AppleVkService))]
namespace Login.iOS
{
    public class AppleVkService : NSObject, IVkService, IVKSdkDelegate, IVKSdkUIDelegate
    {
        readonly string[] _permissions = {
            VKPermissions.Email,
            VKPermissions.Offline
        };

        LoginResult _loginResult;
        TaskCompletionSource _completionSource;

        public AppleVkService()
        {
            VKSdk.Instance.RegisterDelegate(this);
            VKSdk.Instance.UiDelegate = this;
        }

        public Task Login()
        {
            _completionSource = new TaskCompletionSource();
            VKSdk.Authorize(_permissions);
            return _completionSource.Task;
        }

        public void Logout()
        {
            _loginResult = null;
            _completionSource = null;
        }

        [Export("vkSdkTokenHasExpired:")]
        public void TokenHasExpired(VKAccessToken expiredToken)
        {
            VKSdk.Authorize(_permissions);
        }

        public new void Dispose()
        {
            VKSdk.Instance.UnregisterDelegate(this);
            VKSdk.Instance.UiDelegate = null;
            SetCancelledResult();
        }

        public void AccessAuthorizationFinished(VKAuthorizationResult result)
        {
            if (result?.Token == null)
                SetErrorResult(result?.Error?.LocalizedDescription ?? @"VK authorization unknown error");
            else
            {
                _loginResult = new LoginResult
                {
                    Token = result.Token.AccessToken,
                    UserId = result.Token.UserId,
                    Email = result.Token.Email,
                    ExpireAt = Utils.FromMsDateTime(result.Token.ExpiresIn),
                };
                Task.Run(GetUserInfo);
            }
        }

        async Task GetUserInfo()
        {
            var request = VKApi.Users.Get(NSDictionary.FromObjectAndKey((NSString)@"photo_400_orig", VKApiConst.Fields));
            var response = await request.ExecuteAsync();
            var users = response.ParsedModel as VKUsersArray;
            var account = users?.FirstObject as VKUser;
            if (account != null && _loginResult != null)
            {
                _loginResult.FirstName = account.first_name;
                _loginResult.LastName = account.last_name;
                _loginResult.ImageUrl = account.photo_400_orig;
                _loginResult.LoginState = LoginState.Success;
                SetResult(_loginResult);
            }
            else
                SetErrorResult(@"Unable to complete the request of user info");
        }

        public void UserAuthorizationFailed()
        {
            SetErrorResult(@"VK authorization unknown error");
        }

        public void ShouldPresentViewController(UIViewController controller)
        {
            Device.BeginInvokeOnMainThread(() => Utils.GetCurrentViewController().PresentViewController(controller, true, null));
        }

        public void NeedCaptchaEnter(VKError captchaError)
        {
            Device.BeginInvokeOnMainThread(() => VKCaptchaViewController.Create(csaptchaError).PresentIn(Utils.GetCurrentViewController()));
        }

        void SetCancelledResult()
        {
            SetResult(new LoginResult { LoginState = LoginState.Canceled });
        }

        void SetErrorResult(string errorString)
        {
            SetResult(new LoginResult { LoginState = LoginState.Failed, ErrorString = errorString });
        }

        void SetResult(LoginResult result)
        {
            _completionSource?.TrySetResult(result);
            _loginResult = null;
            _completionSource = null;
        }
}

Реализация для Android


Для Android тоже ничего необычного.
[assembly: Dependency(typeof(AndroidVkService))]
namespace Login.Droid
{
    public class AndroidVkService : Java.Lang.Object, IVkService
    {
        public static AndroidVkService Instance => DependencyService.Get() as AndroidVkService;

        readonly string[] _permissions = {
            VKScope.Email,
            VKScope.Offline
        };

        TaskCompletionSource _completionSource;
        LoginResult _loginResult;

        public Task Login()
        {
            _completionSource = new TaskCompletionSource();
            VKSdk.Login(Forms.Context as Activity, _permissions);
            return _completionSource.Task;
        }

        public void Logout()
        {
            _loginResult = null;
            _completionSource = null;
            VKSdk.Logout();
        }

        public void SetUserToken(VKAccessToken token)
        {
            _loginResult = new LoginResult
            {
                Email = token.Email,
                Token = token.AccessToken,
                UserId = token.UserId,
                ExpireAt = Utils.FromMsDateTime(token.ExpiresIn)
            };

            Task.Run(GetUserInfo);
        }

        async Task GetUserInfo()
        {
            var request = VKApi.Users.Get(VKParameters.From(VKApiConst.Fields, @"photo_400_orig,"));
            var response = await request.ExecuteAsync();
            var jsonArray = response.Json.OptJSONArray(@"response");
            var account = jsonArray?.GetJSONObject(0);
            if (account != null && _loginResult != null)
            {
                _loginResult.FirstName = account.OptString(@"first_name");
                _loginResult.LastName = account.OptString(@"last_name");
                _loginResult.ImageUrl = account.OptString(@"photo_400_orig");
                _loginResult.LoginState = LoginState.Success;
                SetResult(_loginResult);
            }
            else
                SetErrorResult(@"Unable to complete the request of user info");
        }

        public void SetErrorResult(string errorMessage)
        {
            SetResult(new LoginResult { LoginState = LoginState.Failed, ErrorString = errorMessage });
        }

        public void SetCanceledResult()
        {
            SetResult(new LoginResult { LoginState = LoginState.Canceled });
        }

        void SetResult(LoginResult result)
        {
            _completionSource?.TrySetResult(result);
            _loginResult = null;
            _completionSource = null;
        }
    }
}

Подключаем в Xanarin.Forms


Всё. ВКонтакте работает!

32e8d286e93344d497fb7887ed808daf.jpg

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

Используем


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

Полный код проекта с подключенными нативными SDK для Facebook и ВКонтакте можете найти по адресу.

В следующей статье мы рассмотрим универсальные способы авторизации пользователей через OAuth в приложениях Xamarin.Forms. Оставайтесь на связи, задавайте ваши вопросы в комментариях и вступайте в группу Xamarin Developers в Telegram.

Об авторе


f70e9ce7a2bd45a98e19652a08b15e26.JPG Вячеслав Черников — руководитель отдела разработки компании Binwell. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone. Статьи Вячеслава вы также можете прочитать в блоге на Medium.

Xamarin Meetup: Сложные интерфейсы в Xamarin.Forms


Если вы хотите пообщаться с сообществом Xamarin Developers, а также лично с Вячеславом, 9 марта в Москве пройдёт митап по теме «Сложные интерфейсы в Xamarin Forms».

Программа митапа:

18:00 — 18:30 Регистрация
18:30 — 19:30 Вячеслав Черников // Подключение RecyclerView/UICollectionView для вёрстки сложных интерфейсов
19:30 — 19:40 Кофе-брейк
19:40 — 20:40 Юрий Невалённый // Коллекции и списки на Xamarin Forms, паттерны виртуализации и быстрые ячейки (Android, iOS, Windows)

Участие бесплатно, регистрация обязательна.

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

© Habrahabr.ru