Підключаємо Facebook SDK для Xamarin.Forms

Соціальні мережі, і особливо Facebook, вже давно використовуються в мобільних додатках. Сьогодні ми розглянемо, як підключити нативні Facebook SDK до проекту на базі Xamarin.Forms (iOS і Android) для зручної авторизації користувачів і отримання про них базової інформації. Ви також легко зможете розширити описані в статті методи для того, щоб реалізувати повноцінну взаємодію з цим чудовим сервісом. Тема проста і зрозуміла, тому без теорій і прелюдій перейдемо відразу до практики.



Створюємо додаток в Facebook
Для тих, хто вперше створює свій додаток в Facebook, ми коротко розповімо про те, як це робиться.

Сам по собі процес це досить простою і зажадає від вас наступних даних:

  • Package Name для Android-проекту (наприклад,
    com.binwell.login
    )
  • Bundle Identifier для iOS-проекту (наприклад,
    com.binwell.login
    )
Для Android ще потрібні Key Hashes, які можна отримати командою:

Windows:

keytool -exportcert -alias androiddebugkey -storepass android -keystore C:\Users\[ІМ'Я користувача]\AppData\Local\Xamarin\Mono для Android\debug.keystore | openssl sha1 -binary | openssl base64

macOS:

keytool -exportcert -alias androiddebugkey -storepass android -keystore /Users/[USERNAME]/.local/share/Xamarin/Mono для Android/debug.keystore | openssl sha1 -binary | openssl base64

Замість
[USERNAME]
необхідно підставити ваше ім'я користувача в системі. Плюс можна прописати шлях до
openssl
, якщо шлях до нього не вказаний
PATH
. Завантажити
openssl
для Windows можна тут.

На виході ми отримаємо потрібні
Key Hashes
наступного виду:
kGP2WMxohvxm/NiwR7H+Eb3/8qw=


Тепер заходимо на сайт і створюємо новий додаток. Окремо для iOS і Android. При створенні програми ми можемо використовувати режим з підказками (Quick Start), де додатково описано як налаштувати проект. З цього керівництва нам і потрібні приклади коду.





Підключаємо Facebook SDK до проектів iOS і Android
Для початку необхідно встановити пакети Facebook SDK від Xamarin для iOS і Android з Nuget:



Зверніть увагу, що з Xamarin.Forms 2.3 на поточний момент сумісна тільки версія Xamarin.Facebook.Android 4.11.0.1. Версія Xamarin.Facebook.iOS обмежень щодо сумісності не має.

Підключаємо в Android

Для початку нам необхідно прописати спеціальні значення у файлі
Resources/values/strings.xml
:

<string name="facebook_app_id">1102463466549096</string>
<string name="fb_login_protocol_scheme">fb1102463466549096</string>

Де,
1102463466549096
це ваш App ID з налаштувань програми Facebook. Додатково нам потрібно внести наступні зміни до
AndroidManifest.xml
:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
<application android:label="@string/app_name">
<activity android:name="com.facebook.FacebookActivity" android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:label="@string/app_name" />
<activity android:name="com.facebook.CustomTabActivity" 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/fb_login_protocol_scheme" />
</intent-filter>
<provider android:authorities="com.facebook.app.FacebookContentProvider1102463466549096" android:name="com.facebook.FacebookContentProvider" android:exported="true" />
</activity>
</application>

Далі вносимо невеликі доопрацювання
MainActivity.cs
:

protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;

base.OnCreate(bundle);
FacebookSdk.SdkInitialize(ApplicationContext);
Forms.Init(this, bundle);
LoadApplication(new App());
}

protected override void OnResume()
{
base.OnResume();
AppEventsLogger.ActivateApp(Application);
}

На цьому первинна ініціалізація Facebook SDK завершена.

Підключаємо в iOS

За аналогією з Android, нам буде необхідно внести зміни у файл
Info.plist
, вставити наступні рядки між
...
:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb1102463466549096</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>1102463466549096</string>
<key>FacebookDisplayName</key>
<string>Binwell Social Demo</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>
<string>fb-messenger-api</string>
<string>fbauth2</string>
<string>fbshareextension</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>facebook.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardsecrecy</key>
<false/>
</dict>
<key>fbcdn.net</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardsecrecy</key>
<false/>
</dict>
<key>akamaihd.net</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSThirdPartyExceptionRequiresForwardsecrecy</key>
<false/>
</dict>
</dict>
</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);

return base.FinishedLaunching(app, options);
}

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

public override void OnActivated(UIApplication application)
{
Facebook.CoreKit.AppEvents.ActivateApp();
}

На цьому попередня підготовка завершена і ми можемо переходити до використання Facebook SDK в нашому додатку.

Інтегруємо з Xamarin.Forms
Використовувати Facebook SDK ми будемо через механізм
DependencyService
. Для цього в першу чергу опишемо потрібні дані і інтерфейс сервісу:

public interface IFacebookService
{
Task<LoginResult> Login();
void Logout);
}

public enum LoginState
{
Failed,
Canceled,
Success
}

public class LoginResult
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ImageUrl { get; set; }
public string UserId { get; set; }
public string Token { get; set; }
public DateTimeOffset ExpireAt { get; set; }
public LoginState LoginState { get; set; }
public string ErrorString { get; set; }
}

Однією з цілей підключення соціальних мереж є можливість простого і зручного отримання даних, тому нам буде потрібно додатковий запит для отримання email, що Facebook не віддає за замовчуванням. Дані запити буде необхідно реалізувати окремо для кожної платформи.

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

Для Android реалізація інтерфейсу виглядає наступним чином:

[assembly: Dependency(typeof(AndroidFacebookService))]
namespace Login.Droid
{
public class AndroidFacebookService: Java.Lang.Object, IFacebookService, GraphRequest.IGraphJSONObjectCallback, GraphRequest.ICallback, IFacebookCallback
{
public static AndroidFacebookService Instance => DependencyService.Get<IFacebookService>() as AndroidFacebookService;

readonly ICallbackManager _callbackManager = CallbackManagerFactory.Create();
readonly string[] _permissions = { @"public_profile", @"email", @"user_about_me" };

LoginResult _loginResult;
TaskCompletionSource<LoginResult> _completionSource;

public AndroidFacebookService()
{
LoginManager.Instance.RegisterCallback(_callbackManager, this);
}

public Task<LoginResult> Login()
{
_completionSource = new TaskCompletionSource<LoginResult>();
LoginManager.Instance.LogInWithReadPermissions(Forms.Context Activity as, _permissions);
return _completionSource.Task;
}

public void Logout)
{
LoginManager.Instance.LogOut);
}

public void OnActivityResult(int requestCode, int resultCode, Intent data)
{
_callbackManager?.OnActivityResult(requestCode, resultCode, data);
}

public void OnCompleted(JSONObject data, GraphResponse response)
{
OnCompleted(response);
}

public void OnCompleted(GraphResponse response)
{
if (response?.JSONObject == null)
_completionSource?.TrySetResult(new LoginResult {LoginState = LoginState.Canceled});
else
{
_loginResult = new LoginResult
{
FirstName = Profile.CurrentProfile.FirstName,
LastName = Profile.CurrentProfile.LastName,
Email = response.JSONObject.Has("email") ? response.JSONObject.GetString("email") : string.Empty,
ImageUrl = response.JSONObject.GetJSONObject("picture")?.GetJSONObject("data")?.GetString("url"),
Token = AccessToken.CurrentAccessToken.Token,
UserId = AccessToken.CurrentAccessToken.UserId,
ExpireAt = FromJavaDateTime(AccessToken.CurrentAccessToken?.Expires?.Time),
LoginState = LoginState.Success
};

_completionSource?.TrySetResult(_loginResult);
}
}

public void OnCancel()
{
_completionSource?.TrySetResult(new LoginResult { LoginState = LoginState.Canceled });
}

public void OnError(FacebookException exception)
{
_completionSource?.TrySetResult(new LoginResult
{
LoginState = LoginState.Failed,
ErrorString = exception?.Message
});
}

public void OnSuccess(Java.Lang.Object result)
{
var facebookLoginResult = result.JavaCast<Xamarin.Facebook.Login.LoginResult>();
if (facebookLoginResult == null) return;

var parameters = new Bundle();
parameters.PutString("fields", "id,email,picture.type(large)");
var request = GraphRequest.NewMeRequest(facebookLoginResult.AccessToken, this);
request.Parameters = parameters;
request.ExecuteAsync();
}

static DateTimeOffset FromJavaDateTime(long? longTimeMillis)
{
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return longTimeMillis != null ? epoch.AddMilliseconds(longTimeMillis.Value) : DateTimeOffset.MinValue;
}
}
}

Додатково потрібно додати обробник
MainActivity.cs
:

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
AndroidFacebookService.Instance.OnActivityResult(requestCode, (int)resultCode, data);
}

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

Робимо реалізацію інтерфейсу для Facebook iOS SDK.

[assembly: Dependency(typeof(AppleFacebookService))]
namespace Login.iOS
{
public class AppleFacebookService: IFacebookService
{
readonly LoginManager _loginManager = new LoginManager();
readonly string[] _permissions = { @"public_profile", @"email", @"user_about_me" };

LoginResult _loginResult;
TaskCompletionSource<LoginResult> _completionSource;

public Task<LoginResult> Login()
{
_completionSource = new TaskCompletionSource<LoginResult>();
_loginManager.LogInWithReadPermissions(_permissions, GetCurrentViewController(), LoginManagerLoginHandler);
return _completionSource.Task;
}

public void Logout)
{
_loginManager.LogOut);
}

void LoginManagerLoginHandler(LoginManagerLoginResult result, NSError error)
{
if (result.IsCancelled)
_completionSource.TrySetResult(new LoginResult {LoginState = LoginState.Canceled});
else if (error != null)
_completionSource.TrySetResult(new LoginResult { LoginState = LoginState.Failed, ErrorString = error.LocalizedDescription });
else
{
_loginResult = new LoginResult
{
Token = result.Token.TokenString,
UserId = result.Token.UserID,
ExpireAt = result.Token.ExpirationDate.ToDateTime()
};

var request = new GraphRequest(@"me", new NSDictionary(@"fields", @"email"));
request.Start(GetEmailRequestHandler);
}
}

void GetEmailRequestHandler(GraphRequestConnection connection, NSObject result, NSError error)
{
if (error != null)
_completionSource.TrySetResult(new LoginResult { LoginState = LoginState.Failed, ErrorString = error.LocalizedDescription });
else
{
_loginResult.FirstName = Profile.CurrentProfile.FirstName;
_loginResult.LastName = Profile.CurrentProfile.LastName;
_loginResult.ImageUrl = Profile.CurrentProfile.ImageUrl(ProfilePictureMode.Square, new CGSize()).ToString();

var dict = result as NSDictionary;
var emailKey = new NSString(@"email");
if (dict != null && dict.ContainsKey(emailKey))
_loginResult.Email = dict[emailKey]?.ToString();

_loginResult.LoginState = LoginState.Success;
_completionSource.TrySetResult(_loginResult);
}
}

static UIViewController GetCurrentViewController()
{
var viewController = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (viewController.PresentedViewController != null)
viewController = viewController.PresentedViewController;
return viewController;
}
}
}

Підключаємо в Xamarin.Forms

Для доступу до створених реалізацій досить вставити наступний обробник події
Clicked
для кнопки «Facebook Login»:

var loginResult = await DependencyService.Get<IFacebookService>().Login();

switch (loginResult.LoginState)
{
case LoginState.Canceled:
// Обробити
break;
case LoginState.Success:
var str = $"Hi {loginResult.FirstName}! Your email is {loginResult.Email}";
break;
default:
// Опрацювати помилки
break;
}

На цьому кодування завершено!

Використовуємо
Отож, хвилюючий момент. Робимо збірку, запускаємо і… легко авторізуємось з допомогою нативних SDK.



Повний код проекту з покроковими змінами розташований у репозиторії на Bitbucket.

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

Залишайтеся на зв'язку, задавайте ваші питання в коментарях і вступайте в групу Xamarin Developers Telegram!

Про автора
В'ячеслав Черніков — керівник відділу розробки компанії Binwell. В минулому — один з Nokia Champion і Qt Certified Specialist, в даний час — спеціаліст по платформах Xamarin і Azure. У сферу mobile прийшов в 2005 році, з 2008 року займається розробкою мобільних додатків: починав з Symbian, Maemo, Meego, Windows Mobile, потім перейшов на iOS, Android і Windows Phone.

Попередні частини
1. Швидке створення MVP (minimum viable product) на базі Microsoft Azure і Xamarin.Forms.
2. Готуємо Xamarin.Forms: налаштування оточення і перші кроки.
3. Підвищуємо ефективність роботи в Xamarin.Forms.
4. Працюємо з станами екранів в Xamarin.Forms.
5. Зручний REST для Xamarin-додатків.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.