1. What is Firebase?

Firebase is Google’s cloud platform. It includes different services such as login, database, hosting…and the most well known; push notifications.

In todays post we are going to focus on the login and database service and how to use it with an application in Xamarin.Forms (Android and iOS).

  1. Why Firebase?

Normally, when an APP is developed we need a pararelo development of an API for the authentication of the user and to obtain/modify the data in a DB. Using Firebase we can eliminate that API development and use the services it offers, so the cost of the total development of the APP is less, in both time and money.

In todays post we will look at how to create an APP with login, and a list of tasks which we can add and remove once inside.

  1. Firebase configuration

To configure Firebase, we must first go to the Firebase developer console, access https://firebase.google.com, and create a project.

Next, you have to create the Android application, entering the name of the package. The SHA-1 debug signature certificate is optional, but it is advisable to create one and add it. The second step is to download the file goolge-service.json, which will be used later.

In the previous step we created the iOS application, but in this case the file we must download is GoogleService-Info.plist. If we lose these files we can download them again by accessing the configuration of the Firebase project.

We can access the Authentication tab of our project to see all the authentication methods we have and enable Google.

In the Database tab, we then create a new database for our project. We select “Realtime Database” and we can see the URL that we will use to access our database.

You have now configured Firebase!

  1. Configuration for Android (Xamarin.Forms)

The first step is to add the Nugets packages. First, we added Google Play Services to the project by right clicking on the project’s package folder, and selected to add the Google Play Services service. This opens a window in which we select Firebase-Auth and we accept. You must also add the Xamarin.GooglePlayServices.Auth and Xamarin.Firebase.Database packages.

The next step is to add the Google-Services.json file, which we downloaded earlier. Once added, we click on the right button and we select GoogleServicesJson in compile action.

Finally, we initialize Firebase in the App by adding the following lines in the OnCreate method of the MainActivity:

var options = new FirebaseOptions.Builder ()

.SetApplicationId ({application ID})

.SetApiKey ({Web API key})

.SetDatabaseUrl ({URL Realtime Database})

.Build ();

if (app == null)

app = FirebaseApp.InitializeApp (this, options, {Project name});

The data “Application ID” and “Web API key” are obtained from the Firebase Android project configuration, and “URL Realtime Database” from the Firebase DB.

  1. Configuration for iOS (Xamarin.Forms)

As in Android, the first thing is to add the necessary packages. For iOS, this includes: Xamarin.Firebase.iOs.Auth, Xamarin.Auth and Xamarin.Firebase.iOS.Database.

We also have to add the file downloaded from Firebase, but in this case GoogleService-Info.plist, and we will change the compilation action to BundleResource.

In the Entitlements.plist file you have to enable key chain to be able to save credentials in our iOS application.

In the Info.plist file we have to add an entry for Google authentication, introducing the application identifier and the REVERSED_CLIENT_ID value of the GoogleService-Info.plist file.

Only the iOS compilation options add –registrar: static in the Additional mtouch Arguments field.

As in Android, we initialized Firebase in the iOS project. In this case we add the following line in the FinishedLaunching method of AppDelegate:FinishedLaunching del AppDelegate:

Firebase.Core.App.Configure();

  1. Authentication with Firebase

For the authentication of our App we create an interface with the necessary methods, which will be used in the ViewModel.

VIEW CODE

public interface IFirebaseAuthService
{
String getAuthKey();
bool IsUserSigned();
Task<bool> SignUp(String email, String password);
Task<bool> SignIn(String email, String password);
void SignInWithGoogle();
Task<bool> SignInWithGoogle(String token);
Task<bool> Logout();
String GetUserId();
}

public class FirebaseAuthService : IFirebaseAuthService
{
public static int REQ_AUTH = 9999;
public static String KEY_AUTH = "auth";

public bool IsUserSigned()
{
var user = Firebase.Auth.FirebaseAuth

.GetInstance(MainActivity.app).CurrentUser;
var signedIn = user != null;
return signedIn;
}

public async Task<bool> SignUp(string email, string password)
{
try
{
await Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app)

.CreateUserWithEmailAndPasswordAsync(email, password);

return true;
}
catch (Exception)
{
return false;
}
}

public async Task<bool> SignIn(string email, string password)
{
try
{
await Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app)

.SignInWithEmailAndPasswordAsync(email, password);
return true;
}
catch (Exception)
{
return false;
}
}

public void SignInWithGoogle()
{
var google = new Intent(Forms.Context,typeof(GoogleLoginActivity));
((Activity)Forms.Context).StartActivityForResult(google, REQ_AUTH);
}

public async Task<bool> SignInWithGoogle(string token)
{
try
{
AuthCredential credential = GoogleAuthProvider.GetCredential

(token, null);
await Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app)

.SignInWithCredentialAsync(credential);
return true;
}
catch (Exception)
{
return false;
}
}

public async Task<bool> Logout()
{
try
{
Firebase.Auth.FirebaseAuth.GetInstance(MainActivity.app)

.SignOut();
return true;
}
catch (Exception)
{
return false;
}
}

public string getAuthKey()
{
return KEY_AUTH;
}

public string GetUserId()
{
var user = Firebase.Auth.FirebaseAuth.GetInstance

(MainActivity.app).CurrentUser;
return user.Uid;
}
}

To gain Google authentification it is necessary to create a new class “GoogleLoginActivity”, which will be the one that shows the screen where we interact with Google.

VIEW CODE

[Activity(Label = "GoogleLogin", Theme = "@style/Theme.AppCompat.Light.DarkActionBar")]
public class GoogleLoginActivity : AppCompatActivity, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
{
const string TAG = "GoogleLoginActivity";

const int RC_SIGN_IN = 9001;

const string KEY_IS_RESOLVING = "is_resolving";
const string KEY_SHOULD_RESOLVE = "should_resolve";

static GoogleApiClient mGoogleApiClient;

bool mIsResolving = false;

bool mShouldResolve = false;

private static GoogleSignInAccount mAuth;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);

String token = {ID de cliente web}
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken(token)
.Build();

mGoogleApiClient = new GoogleApiClient.Builder(this)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.Build();

Intent signInIntent = Auth.GoogleSignInApi.GetSignInIntent(mGoogleApiClient);
StartActivityForResult(signInIntent, RC_SIGN_IN);
}

private void HandleResult(GoogleSignInAccount result)
{
if (result != null)
{
Intent myIntent = new Intent(this, typeof(GoogleLoginActivity));
myIntent.PutExtra("result", result);
SetResult(Result.Ok, myIntent);
}
Finish();
}

private async void UpdateData(bool isSignedIn)
{
if (isSignedIn)
{
HandleResult(mAuth);
}
else
{
await System.Threading.Tasks.Task.Delay(2000);
mShouldResolve = true;
mGoogleApiClient.Connect();
}
}

protected override void OnStart()
{
base.OnStart();
mGoogleApiClient.Connect();
}

protected override void OnStop()
{
base.OnStop();
mGoogleApiClient.Disconnect();
}

protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutBoolean(KEY_IS_RESOLVING, mIsResolving);
outState.PutBoolean(KEY_SHOULD_RESOLVE, mIsResolving);
}

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

if (requestCode == RC_SIGN_IN)
{
var result = Android.Gms.Auth.Api.Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
if (result.IsSuccess)
{
// Google Sign In was successful, authenticate with Firebase
HandleResult(result.SignInAccount);

}
else
{
// Google Sign In failed, update UI appropriately
HandleResult(null);
}
}
}

public void OnConnected(Bundle connectionHint)
{
UpdateData(false);
}

public void OnConnectionSuspended(int cause) { }

public void OnConnectionFailed(ConnectionResult result)
{
if (!mIsResolving && mShouldResolve)
{
if (result.HasResolution)
{
try
{
result.StartResolutionForResult(this, RC_SIGN_IN);
mIsResolving = true;
}
catch (IntentSender.SendIntentException e)
{
mIsResolving = false;
mGoogleApiClient.Connect();
}
}
else
{
ShowErrorDialog(result);
}
}
else
{
UpdateData(false);
}
}

class DialogInterfaceOnCancelListener : Java.Lang.Object, IDialogInterfaceOnCancelListener
{
public Action<IDialogInterface> OnCancelImpl { get; set; }

public void OnCancel(IDialogInterface dialog)
{
OnCancelImpl(dialog);
}
}

void ShowErrorDialog(ConnectionResult connectionResult)
{
int errorCode = connectionResult.ErrorCode;

if (GoogleApiAvailability.Instance.IsUserResolvableError(errorCode))
{

var listener = new DialogInterfaceOnCancelListener();
listener.OnCancelImpl = (dialog) =>
{

mShouldResolve = false;
};
GoogleApiAvailability.Instance.GetErrorDialog(this, errorCode, RC_SIGN_IN, listener).Show();
}
else
{
mShouldResolve = false;
}
HandleResult(mAuth);
}
}

The field “Web client ID” is obtained from Firebase. In the Authentication tab, we select Google and you can view it in “Configuring the web SDK”.

To collect the result of the previously created Activity you have to add the OnActivityResult method of the MainActivity.

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == FirebaseAuthService.REQ_AUTH && resultCode == Result.Ok)
{
GoogleSignInAccount sg = (GoogleSignInAccount)data.GetParcelableExtra(“result”);
MessagingCenter.Send(FirebaseAuthService.KEY_AUTH, FirebaseAuthService.KEY_AUTH, sg.IdToken);
}
}

iOS:

The implementation of the interface is:

VIEW CODE

public class FirebaseAuthService : IFirebaseAuthService
{
public static String KEY_AUTH = "auth";
public static OAuth2Authenticator XAuth;
private static bool hasLoginResult = false;
private static bool loginResult = false;
private static bool signUpResult = false;
CancellationTokenSource tokenSource ;
CancellationToken token;
Task t;

public IntPtr Handle => throw new NotImplementedException();

public string getAuthKey()
{
return KEY_AUTH;
}

public bool IsUserSigned()
{
var user = Auth.DefaultInstance.CurrentUser;
return user != null;
}

public async Task<bool> Logout()
{
NSError error;
var signedOut = Auth.DefaultInstance.SignOut(out error);

if (!signedOut)
{
return false;
}

return true;
}

//LOGIN USER/PASS
public async Task<bool> SignIn(string email, string password)
{
Auth.DefaultInstance.SignIn(email, password, HandleAuthResultLoginHandler);
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
t = Task.Factory.StartNew(async () =>
{
await Task.Delay(4000);
}, token).Unwrap();
await t;

return loginResult;
}

private void HandleAuthResultLoginHandler(User user, Foundation.NSError error)
{
if (error != null)
{
loginResult = false;
hasLoginResult = true;
}
else
{
loginResult = true;
hasLoginResult = true;
}
tokenSource.Cancel();
}

//COMPROBAR TOKEN LOGIN GOOGLE
public async Task<bool> SignInWithGoogle(string tokenId)
{
String[] tokens = tokenId.Split(new string[] { "###" }, StringSplitOptions.None);
var credential = GoogleAuthProvider.GetCredential(tokens[0], tokens[1]);
Auth.DefaultInstance.SignIn(credential, HandleAuthResultHandlerGoogleSignin);
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
t = Task.Factory.StartNew(async () =>
{
await Task.Delay(4000);
}, token).Unwrap();
await t;

return loginResult;
}

private void HandleAuthResultHandlerGoogleSignin(User user, NSError error)
{
if (error != null)
{
loginResult = false;
hasLoginResult = true;
}
else
{
loginResult = true;
hasLoginResult = true;
}

tokenSource.Cancel();
}

// LOGIN GOOLE
public void SignInWithGoogle()
{
XAuth = new OAuth2Authenticator(
clientId: "{ClientId}",
clientSecret: "",
scope: "profile",
authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
redirectUrl: new Uri("{RedirectUrl}"),
accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token"),
isUsingNativeUI: true);

var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
XAuth.Completed += OnAuthenticationCompleted;

XAuth.Error += OnAuthenticationFailed;

var viewController = XAuth.GetUI();
vc.PresentViewController(viewController, true, null);
}

private void OnAuthenticationCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
vc.DismissViewController(true, null);

if (e.IsAuthenticated)
{
var access_token = e.Account.Properties["access_token"].ToString();
var id_token = e.Account.Properties["id_token"].ToString();

MessagingCenter.Send(FirebaseAuthService.KEY_AUTH, FirebaseAuthService.KEY_AUTH, id_token + "###" + access_token);
}
else
{
//Error
}
}

private void OnAuthenticationFailed(object sender, AuthenticatorErrorEventArgs e)
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
vc.DismissViewController(true, null);
}

// REGISTRO USUARIO
public async Task<bool> SignUp(string email, string password)
{
Auth.DefaultInstance.CreateUser(email, password,HandleAuthResultHandlerSignUp);
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
t = Task.Factory.StartNew(async () =>
{
await Task.Delay(4000);
}, token).Unwrap();
await t;
return signUpResult;
}

private void HandleAuthResultHandlerSignUp(User user, Foundation.NSError error)
{
if (error != null)
{
signUpResult = false;
hasLoginResult = true;
}
else
{
signUpResult = true;
hasLoginResult = true;
}
tokenSource.Cancel();
}

public string GetUserId()
{
var user = Auth.DefaultInstance.CurrentUser;
return user.Uid;
}
}

The ClientId field is obtained from the GoogleService-Info.plist file, and the RedirectUrl field is the union of the REVERSE_CLIENT_ID value of the same file and then “: / oauth2redirect” is added.

In Android an Activity was created for the Login with Google, but in iOS an OAuth2Authenticator is used, which opens a browser window. To obtain the result of the authentication, two functions must be added to the AppDelegate.

VIEW CODE

// For iOS 9 or newer
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
Uri uri_netfx = new Uri(url.AbsoluteString);

// load redirect_url Page for parsing
FirebaseAuthService.XAuth.OnPageLoading(uri_netfx);

return true;

}

// For iOS 8 and older
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
Uri uri_netfx = new Uri(url.AbsoluteString);

// load redirect_url Page for parsing
FirebaseAuthService.XAuth.OnPageLoading(uri_netfx);

return true;
}

With this, it is only necessary to create a view and use the service implemented in the ViewModel to have authentication with Firebase completed.

  1. Firebase database

As for authentication, the first thing to do is to create a service interface, which will be implemented in each platform and can therefore be used in the ViewModel.

public interface IFirebaseDBService
{
void Connect();
void GetMessage();
void SetMessage(String message);
string GetMessageKey();
void DeleteItem(string key);
}

If you want to work with the data, all you have to do is implement the interface in each platform, create a view with your ViewMode and use the service in it.

Android:

VIEW CODE

 [assembly: Dependency(typeof(FirebaseDBService))]
namespace firebasesample.Droid.Services.FirebaseDB
{
public class ValueEventListener : Java.Lang.Object, IValueEventListener
{
public void OnCancelled(DatabaseError error) { }

public void OnDataChange(DataSnapshot snapshot) {
ObservableCollection<Homework> list = new ObservableCollection<Homework>();

foreach (DataSnapshot item in snapshot.Children.ToEnumerable<DataSnapshot>())
{
list.Add(new Homework
{
Key = item.Key.ToString(),
HomeWork = item.Value.ToString()
});

}

MessagingCenter.Send(FirebaseDBService.KEY_MESSAGE, FirebaseDBService.KEY_MESSAGE, list);
}
}

public class FirebaseDBService : IFirebaseDBService
{
DatabaseReference databaseReference;
FirebaseDatabase database;
FirebaseAuthService authService = new FirebaseAuthService();
public static String KEY_MESSAGE = "items";

public void Connect()
{
database = FirebaseDatabase.GetInstance(MainActivity.app);
}

public void GetMessage()
{
var userId = authService.GetUserId();
databaseReference = database.GetReference("items/" + userId);
databaseReference.AddValueEventListener(new ValueEventListener());
}

public string GetMessageKey()
{
return KEY_MESSAGE;
}

public void SetMessage(string message)
{
var userId = authService.GetUserId();
databaseReference = database.GetReference("items/" + userId);

String key = databaseReference.Push().Key;

databaseReference.Child(key).SetValue(message);
}

public void DeleteItem(string key)
{
var userId = authService.GetUserId();
databaseReference = database.GetReference("items/" + userId);
databaseReference.Child(key).RemoveValue();
}
}
}

 iOS:

VIEW CODE

 [assembly: Dependency(typeof(FirebaseDBService))]
namespace firebasesample.iOS.Services.FirebaseDB
{
public class FirebaseDBService : IFirebaseDBService
{
public static String KEY_MESSAGE = "items";

private FirebaseAuthService authService = new FirebaseAuthService();
DatabaseReference databaseReference;

public void Connect()
{
databaseReference = Database.DefaultInstance.GetRootReference();
}

public void GetMessage()
{
var userId = authService.GetUserId();
var messages = databaseReference.GetChild("items").GetChild(userId);

nuint handleReference2 = messages.ObserveEvent(DataEventType.Value, (snapshot) =>
{
//var folderData = snapshot.GetValue<NSDictionary>();
// Do magic with the folder data
NSObject value = snapshot.GetValue();
ObservableCollection<Homework> list = new ObservableCollection<Homework>();
if (value is NSDictionary folderData)
{

foreach (var item in folderData)
{
list.Add(new Homework
{
Key = item.Key.ToString(),
HomeWork = item.Value.ToString()
});
}
}
MessagingCenter.Send(FirebaseDBService.KEY_MESSAGE, FirebaseDBService.KEY_MESSAGE, list);
});
}

public void SetMessage(String message)
{
var userId = authService.GetUserId();
var messages = databaseReference.GetChild("items").GetChild(userId).Reference;
var key = messages.GetChildByAutoId().Key;
messages.GetChild(key).SetValue((NSString)message);
}

public String GetMessageKey()
{
return KEY_MESSAGE;
}

public void DeleteItem(string key)
{
var userId = authService.GetUserId();
var messages = databaseReference.GetChild("items").GetChild(userId).Reference;
messages.GetChild(key).RemoveValue();
}
}
}

By following these steps and using Firebase, you are able to create an APP that has authentication and data management without replying on an API. However, its important to remember that Firebase isn’t always the best option. If the data model is more complex for example, then it’s better to have an API and a relational database. Keeping that in mind, you’re ready to get started!