📜 ⬆️ ⬇️

Connecting Facebook SDK for Xamarin.Forms

Social networks, and especially Facebook, have long been used in mobile applications. Today we will look at how to connect the native Facebook SDK to a project based on Xamarin.Forms (iOS and Android) for easy authorization of users and getting basic information about them. You can also easily expand the methods described in the article in order to realize full-fledged interaction with this wonderful service. The topic is simple and understandable, therefore, without theories and preludes, we will proceed immediately to practice.



Create an application on Facebook


For those who first create their application in Facebook, we will briefly describe how this is done.

The process itself is quite simple and will require the following data from you:
')

For Android, you still need Key Hashes, which can be obtained with the command:

Windows:

 keytool -exportcert -alias androiddebugkey -storepass android -keystore C:\Users\[USERNAME]\AppData\Local\Xamarin\Mono for Android\debug.keystore | openssl sha1 -binary | openssl base64 

macOS:

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

Instead of [USERNAME] you must substitute your username in the system. Plus, you can register the path to openssl , if the path to it is not specified in the PATH . Download openssl for Windows here .

At the output, we get the necessary Key Hashes following form: kGP2WMxohvxm/NiwR7H+Eb3/8qw=

Now go to developers.facebook.com and create a new application. Separately for iOS and Android. When creating an application, we can use the Quick Start mode, which additionally describes how to set up a project. We will need code examples from this guide.





We connect Facebook SDK to iOS and Android projects


First you need to install the Facebook SDKs from Xamarin for iOS and Android from Nuget:



Please note that only Xamarin.Facebook.Android 4.11.0.1 is currently compatible with Xamarin.Forms 2.3. Version Xamarin.Facebook.iOS has no compatibility limitations.

Connect to Android


First we need to write special values ​​in the file Resources/values/strings.xml :

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

Where, 1102463466549096 is your App ID from the settings of the Facebook application. Additionally, we will need to make the following changes to AndroidManifest.xml :

  <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:label="@string/app_name"> <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/> <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> 

Next, we make minor improvements to 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); } 

This completes the initial initialization of the Facebook SDK.

We connect in iOS


Similar to Android, we will need to make changes to the Info.plist file, insert the following lines between <dict>...</dict> :

 <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> 

And some code in 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(); } 

This completes the preliminary preparation and we can proceed to use the Facebook SDK in our application.

Integrating with Xamarin.Forms


We will use the Facebook SDK through the DependencyService mechanism. To do this, first of all, we describe the necessary data and service interface:

 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; } } 

One of the goals of connecting social networks is the ability to easily and conveniently retrieve user data, so we will need an additional request to receive email, which Facebook does not give by default. These requests will need to be implemented separately for each platform.

Implementation for android


For Android, the implementation of the interface is as follows:

 [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 as Activity, _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; } } } 

Additionally, you need to add a handler to MainActivity.cs :

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

Implementation for iOS


Making interface implementation for 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; } } } 

We connect to Xamarin.Forms


To access the created implementations, simply insert the following Clicked event Clicked for the Facebook Login button:

 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; } 

This coding is complete!

We use


So, an exciting moment. We build, run and ... easily log in using native SDKs.



The complete project code with step changes is located in the Bitbucket repository .

So, today we connected the native Facebook SDK to the application on Xamarin.Forms. Authorization and getting basic information about the user is already working, but if you wish, you can easily expand the set of methods for accessing all the features of the Facebook SDK. Next time we will take the puzzle more interesting and connect the native VKontakte SDK.

Stay in touch, ask your questions in the comments and join the Xamarin Developers group in Telegram!

about the author


Vyacheslav Chernikov - head of development at Binwell . In the past, he was one of the Nokia Champion and Qt Certified Specialists, currently he is the Xamarin and Azure platform specialist. He came to the sphere of mobile in 2005, since 2008 he has been developing mobile applications: he started with Symbian, Maemo, Meego, Windows Mobile, then switched to iOS, Android and Windows Phone.

Other articles by the author:

Source: https://habr.com/ru/post/321454/


All Articles