Pull to refresh
76.6
Rating
Microsoft
Microsoft — мировой лидер в области ПО и ИТ-услуг

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

Microsoft corporate blog .NET *Development of mobile applications *C# *Xamarin *
В прошлой статье мы рассмотрели вопрос с подключением нативных SDK от Facebook в ваших приложениях на Xamarin.Forms для удобной авторизации пользователей. Сегодня, как и обещали, рассмотрим подключение нативных SDK для социальной сети ВКонтакте. Новый SDK будет подключаться к проекту, который мы описывали в прошлой статье.



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


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

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



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



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

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


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



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


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

<key>CFBundleURLTypes</key>
<array>
  <dict>
	  <key>CFBundleURLName</key>
	  <string>vk5874073</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>fb1102463466549096</string>
      <string>vk5874073</string>
    </array>
  </dict>
</array>

Добавляем новые LSApplicationQueriesSchemes:

  <string>vk</string> 
  <string>vk-share</string> 
  <string>vkauthorize</string>

И также новый домен в NSAppTransportSecurity:

<key>vk.com</key> 
      <dict> 
          <key>NSExceptionRequiresForwardSecrecy</key> 
          <false/> 
          <key>NSIncludesSubdomains</key> 
          <true/> 
          <key>NSExceptionAllowsInsecureHTTPLoads</key> 
          <true/> 
      </dict>

После этого вносим правки в 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:

  <integer name="com_vk_sdk_AppId">5874073</integer>
  <string name="vk_data_theme">vk5874073</string>


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

    <activity android:name="com.binwell.login.MainActivity" android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="@string/vk_data_theme" />
      </intent-filter>
    </activity>

И завершим расширением 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<LoginResult> 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<LoginResult> _completionSource;

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

        public Task<LoginResult> Login()
        {
            _completionSource = new TaskCompletionSource<LoginResult>();
            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<IVkService>() as AndroidVkService;

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

        TaskCompletionSource<LoginResult> _completionSource;
        LoginResult _loginResult;

        public Task<LoginResult> Login()
        {
            _completionSource = new TaskCompletionSource<LoginResult>();
            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


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



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

Используем


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

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

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

Об авторе


Вячеслав Черников — руководитель отдела разработки компании 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)

Участие бесплатно, регистрация обязательна.
Tags:
Hubs:
Total votes 27: ↑25 and ↓2 +23
Views 10K
Comments Comments 2

Information

Founded
Location
США
Website
www.microsoft.com
Employees
Unknown
Registered