Very often I hear questions like “Is there any Facebook SDK for Xamarin.Forms? Because there is one for Android Xamarin Native.”. This kind of questions can apply to different SDK that are compatible only with specific platforms and cannot be consumed directly in Xamarin.Forms. The answer to these questions usually is “If there is an available native Xamarin SDK it can be consumed in your Xamarin.Forms project. All you have to do is to create an abstraction like you would do with any other platform specific code.”.
As you might already understand, in this article we will create an abstraction over Xamarin.Facebook.Android and Xamarin.Facebook.iOS in order to display a native Facebook Login Button and handle the authentication related events in our Xamarin.Forms application.
* Please note that this technique can be applied to any Xamarin native SDKs.
Let’s create our PCL Xamarin.Forms project. It should have 2 targets: Android and iOS. I would highly recommend to switch to .netstandard 2.0 and update all the NuGet packages. Don’t forget that you Android project should target Android 8.1 without it the NuGet update will fail.
Now we have to create a Facebook application on https://developers.facebook.com/. Once created we have to add a “Facebook Login” product. Then we have to create 2 platforms for our application Android and iOS, it can be easily done if you will follow the Quickstart instructions per platform, you can ignore all the steps involving code or configuration files manipulations. Please pay attention to your package name and bundle identifier, they should match your Android and iOS projects. For Android you will have to provide a “Key Hashes”, luckily the Quickstart instructions are nicely explaining how to achieve that. I am on purpose not going into details since it is a very straightforward process, thumbs up for the Facebook team. Also you can easily generate test users for your application on Facebook under Roles > Test Users.
It’s time to write some code!
Xamarin.Forms
Let’s create our abstraction layer, it will be a very simple Xamarin.Forms control:
public class FacebookLoginButton : View
{
public string[] Permissions
{
get { return (string[])GetValue(PermissionsProperty); }
set { SetValue(PermissionsProperty, value); }
}
public static readonly BindableProperty PermissionsProperty =
BindableProperty.Create(
nameof(Permissions),
typeof(string[]),
typeof(FacebookLoginButton),
// This permission is set by default, even if you don’t add it, but FB recommends to add it anyway
defaultValue: new string[] { “public_profile”, “email” });
public Command<string> OnSuccess
{
get { return (Command<string>)GetValue(OnSuccessProperty); }
set { SetValue(OnSuccessProperty, value); }
}
public static readonly BindableProperty OnSuccessProperty =
BindableProperty.Create(nameof(OnSuccess), typeof(Command<string>), typeof(FacebookLoginButton));
public Command<string> OnError
{
get { return (Command<string>)GetValue(OnErrorProperty); }
set { SetValue(OnErrorProperty, value); }
}
public static readonly BindableProperty OnErrorProperty =
BindableProperty.Create(nameof(OnError), typeof(Command<string>), typeof(FacebookLoginButton));
public Command OnCancel
{
get { return (Command)GetValue(OnCancelProperty); }
set { SetValue(OnCancelProperty, value); }
}
public static readonly BindableProperty OnCancelProperty =
BindableProperty.Create(nameof(OnCancel), typeof(Command), typeof(FacebookLoginButton));
}
This View is exposing the next properties:
- Permissions – array of permission to be asked from the FB user account
- OnSuccess – a Command that will return the authentication token and execute when the authentication process will complete
- OnError – a Command that will return the error description and execute when an exception will be thrown by the authentication process
- OnCancel – a Command that will execute when the user will manually cancel the authentication process
Now create a ViewModel
and set it as a BindingContext
of you main / root Page
with a FacebookLoginButton
on it:
public class LoginViewModel
{
public ICommand OnFacebookLoginSuccessCmd { get; }
public ICommand OnFacebookLoginErrorCmd { get; }
public ICommand OnFacebookLoginCancelCmd { get; }
public LoginViewModel()
{
OnFacebookLoginSuccessCmd = new Command<string>(
(authToken) => DisplayAlert(“Success”, $”Authentication succeed: {authToken}“));
OnFacebookLoginErrorCmd = new Command<string>(
(err) => DisplayAlert(“Error”, $”Authentication failed: {err}“));
OnFacebookLoginCancelCmd = new Command(
() => DisplayAlert(“Cancel”, “Authentication cancelled by the user.”));
}
void DisplayAlert(string title, string msg) =>
(Application.Current as App).MainPage.DisplayAlert(title, msg, “OK”);
}
iOS
- Add
Xamarin.Facebook.iOS
NuGet package. - Add the next lines to your
Info.plist
, replacing%APP_ID%
by your FB app id:<key>CFBundleURLTypes</key>
<array><dict><key>CFBundleURLSchemes</key><array><string>fb%APP_ID%</string></array></dict></array><key>FacebookAppID</key><string>%APP_ID%</string><key>FacebookDisplayName</key><string>XFFBLoginExample</string><key>LSApplicationQueriesSchemes</key><array><string>fbapi</string><string>fb-messenger-share-api</string><string>fbauth2</string><string>fbshareextension</string></array> - Your
AppDelegate.cs
should override theOpenUrl
method:
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
// We need to handle URLs by passing them to their own OpenUrl in order to make the SSO authentication works.
return ApplicationDelegate.SharedInstance.OpenUrl(application, url, sourceApplication, annotation);
} - Create a
FacebookLoginButtonRenderer
:
[assembly: ExportRenderer(typeof(FacebookLoginButton), typeof(FacebookLoginButtonRenderer))]
namespace YourNamespace.iOS
{
public class FacebookLoginButtonRenderer : ViewRenderer
{
bool disposed;protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var fbLoginBtnView = e.NewElement as FacebookLoginButton;
var fbLoginbBtnCtrl = new LoginButton
{
LoginBehavior = LoginBehavior.Native,
ReadPermissions = fbLoginBtnView.Permissions
};fbLoginbBtnCtrl.Completed += AuthCompleted;
SetNativeControl(fbLoginbBtnCtrl);
}
}void AuthCompleted(object sender, LoginButtonCompletedEventArgs args)
{
var view = (this.Element as FacebookLoginButton);
if (args.Error != null)
{
// Handle if there was an error
view.OnError?.Execute(args.Error.ToString());}
else if (args.Result.IsCancelled)
{
// Handle if the user cancelled the login request
view.OnCancel?.Execute(null);
}
else
{
// Handle your successful login
view.OnSuccess?.Execute(args.Result.Token.TokenString);
}
}protected override void Dispose(bool disposing)
{
if (disposing && !this.disposed)
{
if (this.Control != null)
{
(this.Control as LoginButton).Completed -= AuthCompleted;
this.Control.Dispose();
}
this.disposed = true;
}
base.Dispose(disposing);
}
}
}
Android
- Add
Xamarin.Facebook.Android
NuGet package. - Create a
strings.xml
under Resources/values, don’t forget to replace%APP_ID%
by FB app id &%APP_NAME%
by your Android application name:
<?xml version=“1.0“ encoding=“utf-8“?>
<resources>
<string name=“app_id“>%APP_ID%</string>
<string name=“app_name“>%APP_NAME%</string>
<string name=“fb_login_protocol_scheme“>fb%APP_ID%</string>
</resources> - In your
AndroidManifest.xml
addInternet
permission. - Add the next lines to your
AndroidManifest.xml
inside the application
tag:
<meta-data
android:name=“com.facebook.sdk.ApplicationId“android:value=“@string/app_id“ />
<activity android:name=“com.facebook.FacebookActivity“
android:configChanges=“keyboard|keyboardHidden|screenLayout|screenSize|orientation” 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>
</activity> - Add a
CallbackManager
to yourMainActivity.cs
and overrideOnActivityResult
:
public static ICallbackManager CallbackManager;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
// Create callback manager using CallbackManagerFactory
CallbackManager = CallbackManagerFactory.Create();
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
protected override void OnActivityResult(
int requestCode,
Result resultCode,
Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
CallbackManager.OnActivityResult(requestCode, Convert.ToInt32(resultCode),
data);
}
- Create a
FacebookLoginButtonRenderer
:
[assembly: ExportRenderer(typeof(FacebookLoginButton), typeof(FacebookLoginButtonRenderer))]
namespace XFFacebookExample.Droid
{
public class FacebookLoginButtonRenderer : ViewRenderer
{
Context ctx;
bool disposed;public FacebookLoginButtonRenderer(Context ctx) : base(ctx)
{
this.ctx = ctx;
}protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
if (Control == null)
{
var fbLoginBtnView = e.NewElement as FacebookLoginButton;
var fbLoginbBtnCtrl = new Xamarin.Facebook.Login.Widget.LoginButton(ctx)
{
LoginBehavior = LoginBehavior.NativeWithFallback
};fbLoginbBtnCtrl.SetReadPermissions(fbLoginBtnView.Permissions);
fbLoginbBtnCtrl.RegisterCallback(MainActivity.CallbackManager, new MyFacebookCallback(this.Element as FacebookLoginButton));SetNativeControl(fbLoginbBtnCtrl);
}
}protected override void Dispose(bool disposing)
{
if (disposing && !this.disposed)
{
if (this.Control != null)
{
(this.Control as Xamarin.Facebook.Login.Widget.LoginButton).UnregisterCallback(MainActivity.CallbackManager);
this.Control.Dispose();
}
this.disposed = true;
}
base.Dispose(disposing);
}class MyFacebookCallback : Java.Lang.Object, IFacebookCallback
{
FacebookLoginButton view;public MyFacebookCallback(FacebookLoginButton view)
{
this.view = view;
}public void OnCancel() =>
view.OnCancel?.Execute(null);public void OnError(FacebookException fbException) =>
view.OnError?.Execute(fbException.Message);public void OnSuccess(Java.Lang.Object result) =>
view.OnSuccess?.Execute(((LoginResult)result).AccessToken.Token);
}
}
}
Conclusion
Our abstraction is a simple custom View
that is rendered on different platforms via custom Renderer
s. The rendering is pretty straightforward, we create an instance of a native FacebookLoginButton
and we listen to platform specific authentication related events to fire our own XF events.
It is a fairly simple solution that demonstrates that you don’t have to avoid using Xamarin Native SDKs just because they are not supported out-of-the-box on Xamarin.Forms
.
The source code is available on github.
P.S.: There is a second part, dedicated QA article for this topic that you can find here. Most popular questions have been answered there.
How much data does Facebook send to its server when you use the SDK?
Is there any way to block all the telemetry data they send?
LikeLike
Hello Jean.
Currently I don’t have an answer for your questions. However, you can monitor the sent data yourself and share the results. The authentication flow with the FB login button may differ and can involve the mobile browser or installed FB app. This may also affect the telemetry data.
LikeLike
Thanks for the answer.
On my side I have implemented a custom login button (not using the SDK) to avoid the issue.
But if I change, this will be a point I will investigate for sure before going live.
LikeLike
Hey, It looks like there is a small issue with the example info.plist.
You’ve not included the CFBundleURLTypes.
Other than that, very helpful, thanks!
LikeLiked by 1 person
Hello Yuri,
Thanks for pointing it out, will be fixed in few moments.
LikeLike
Thanks for the article,
I have a question. How can I Display for example the first name or the email of the user that just logged in?
LikeLike
Hello Ramiro,
In this case you will have to create an abstraction that will expose those properties after authenticating the user. A good start can be to check the official samples of Xamarin.Facebook.Android/iOS : https://github.com/xamarin/FacebookComponents Alternatively, you could check if maybe a better solutions exists by this time.
LikeLike
Excellent article and well done code. Thank you Evgeny!
LikeLike
Hello,
Thanks for this article and this shared code 🙂
But i have an issue when i try to implement it.
I got this exception :
Unhandled Exception:
Xamarin.Facebook.FacebookSdkNotInitializedException: The SDK has not been initialized, make sure to call FacebookSdk.sdkInitialize() first.
But when i read information from the SDK they said that the initialization is automaticaly made and the initialization shouldn’t be done manualy.
Someone could help me ? ^^
LikeLike
Edit :
I found the issue was from the AndroidManifest.xml
I made a mistake on this line ^^
This code is working perfectly thanks you again for this share ^^
LikeLike
Hello Joutouken!
Glad the issue is resolved.
LikeLike
Hi,
Im getting same error can please share which line you have updated?
LikeLike
Hello,
This code is working perfectly, very good work! 🙂
But I have a question.
Can I change the button text?
Thank you in advance,
Zoltán
LikeLike
Hello!
Yes, it is possible.
For iOS use `SetAttributedTitle` and for Android use `setText` on the native control.
Beside that, there is an alternative way that can be found here: https://smellyc0de.wordpress.com/2018/08/09/retrieving-facebook-user-access-token-in-xamarin-forms/
Kind regards
LikeLike
Hello!
I tried it, but it’s not working.
Can you tell me exactly where I can set the text?
Thanks in advance,
Zoltán
LikeLike
Hi I have a question , how can I make it in that way that when I log in with facebook I can go to the next page not on the same page . Thank you
LikeLike
Hello Amra,
You can hook to “OnSuccess” event and then do the navigation part. In case you wonder how to trigger the navigation events on the ViewModel level, there are different ways to do it and it is quite a big topic of it’s own. A good starting point could be to send the Navigation from your page/view to the binding context/view model as a constructor parameter.
Good luck!
LikeLike
Hello,
Thanks for this article and this shared code 🙂
But I have an issue when i try to implement it.
I got this exception :
Unhandled Exception:
Xamarin.Facebook.FacebookSdkNotInitializedException: The SDK has not been initialized, make sure to call FacebookSdk.sdkInitialize() first.
I have checked above someone has suggested updating the manifest, but didn’t specified what need to update please help.
LikeLike
Same issue for me, any update?
LikeLike
Please check the QA article on the following topic: https://evgenyzborovsky.com/2019/09/10/facebook-sdk-qa/
LikeLike
Thank you for your reply,
I checkd the QA article about this issue, I checked again and again strings.xml and AndroidManifest.xml and everything look ok! I Checked also you example project on github to compare and again it looks ok! But I still facing the same issue 😦
When I’m setting info manually on MainActivity.cs like this:
”
FacebookSdk.ApplicationId = 1234567890;
FacebookSdk.ApplicationName = MyApp;
FacebookSdk.SdkInitialize(this);
”
It is working fine! But I have warnings that notify me that I’m using obsolete and deprecated methods !
LikeLike
Can it be that you are using a different version of Facebook SDK?
LikeLike
I’m using nuget “Xamarin.Facebook.Android” latest version (5.0.3) only installed on Android project up to now. Something else to be checked?
LikeLike
Create a reproduction project on github and share it with me. I will take a look.
LikeLike
Done, if you uncomment line “23” on “FacebookLoginExample.Droid.MainActivity.cs” it’s working fine.
Thank you very much for your help!
LikeLike
Glad you resolved the issue!
LikeLike
Thank you so much for your support! It works!
LikeLike
No, sorry bad communication from me, “Done” means “I have share with you a reproduction project on github 😀 I still have the problem, please take a look on the project when you have little bit time…
line 23 (commented on FacebookLoginExample.Droid.MainActivity.cs) is a call to a mehod that call FacebookSdk.sdkInitialize() that is deprecated but solve the problem for me, without that is not working and the following exception is raised:
Xamarin.Facebook.FacebookSdkNotInitializedException: The SDK has not been initialized, make sure to call FacebookSdk.sdkInitialize() first.
Thanks
LikeLike
The problem was in AndroidManifest.xml, application tag was defined twice. I pushed the fix so you could compare it to the original file.
LikeLike
Hello,
Thank you for your great code.
I have a conflit in Android, here is the error
Resolving conflicts for MonoAndroid,Version=v8.1…
NU1107: Version conflict detected for Xamarin.Android.Support.Compat. Reference the package directly from the project to resolve this issue.
SCT_UPS_Test.Android -> Xamarin.Android.Support.v7.MediaRouter 27.0.2.1 -> Xamarin.Android.Support.v7.Palette 27.0.2.1 -> Xamarin.Android.Support.Compat (= 27.0.2.1)
SCT_UPS_Test.Android -> Xamarin.Facebook.Android 4.38.0 -> Xamarin.Facebook.Common.Android 4.38.0 -> Xamarin.Android.Support.CustomTabs 27.0.2 -> Xamarin.Android.Support.Compat (= 27.0.2).
I difn’t add any package but Xamarin.Facebook
LikeLike
Hello,
I’ve resolved this conflict adding this package first: Xamarin.Android.Support.CustomTabs 27.0.2.1
Once you have installed CustomTabs, you will be able to install Xamarin.Facebook.Android.
LikeLike
Hi,
Thanks for your great article.
I am trying to find a way to catch logout event. I need to remove AccessToken that retrieved from Server.
Or it would also work to create a Xamarin.Forms button to trigger Xamarin.Facebook.Android/Ios Logout.
I hope my question is clear. Thanks in advance.
LikeLike
Hey,
I think I was with the same problem that you have, don’t know if you solved it yet, but here is what I did.
To catch the logout event in Android you can call this Static Method: LoginManager.Instance.LogOut();
In iOS you can create a new LoginManager class instance and call this method:
var loginManager = new LoginManager();
loginManager.LogOut();
So I created a Dependency Injection to access the methods in the shared project.
LikeLike
Thanks Evgeny for this great article! This is working for me without any issues, very easy to follow. I do also have this question, I’ve got a logout button in my app, I would like to be able to use this to programatically trigger the facebook logout.
LikeLike
I want to be able to link this to firebase (and also if you would please elaborate on how might I play off the onSuccess event.) If you have time to address one or both of these issues it would be amazing. I have everything set up on firebase and facebook to link together, it is just interacting with your code to authorize it I believe. All the relevant tutorials use android studio, and the ones that use xamarin forms wouldn’t play well with your solution.
LikeLike
Great post! Once I have logged in with Facebook, and then restarted the application all together the button turns to ‘Logout’, how would I check to see the current state of the FB login such that when my app starts up I can either show the login page or some other page depending if the user is already logged in?
LikeLike
When the FB button is rendered, and then I navigate from that page to another page on both iOS and Android I get exceptions. For iOS, in the OnElementChanged method in the FacebookLoginButtonRenderer I added a condition to check if its a FB button before I process the code and this seems to do the trick. if (e.NewElement != null) With the Android, I am kinda stuck in the DIspose method of the FBloginButtonRenderer. Any guidance on what needs to be added for both cases, please?
LikeLike
Hello, if you could share with me a reproduction repo I could take a look.
LikeLike
Would you happen to have something similar for the Apple Login? With the new submissions, if there is facebook, there needs to be Apple Sign in as well.
LikeLike
Good evening Reza,
It is included in official docs: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/sign-in-with-apple/
LikeLike
That isn’t a Xamarin.Forms version so I couldn’t get it to work. I found this, https://github.com/CrossGeeks/signinwithapplesample but it only seems to work when you set a breakpoint at the var creds = await tcsCredential.Task; in the AppleSignInService.cs and step forward at the right time when you sign into the app.
Further, I am trying to figure out how, using this token that I get back, would I get the users Email and Facebook from my Server endpoint. I haven’t found any concrete API call information so I wanted to know if you have any idea.
LikeLike
Is there a way to check if the user is logged into Facebook with your version?
LikeLike
Hello,
Yes, it should be possible, there is a `Facebook.CoreKit.AccessToken.CurrentAccessTokenIsActive` property. There might be also other ways.
LikeLike